diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c46b22b6..740a0a2d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,16 @@ updates: actions: patterns: - "*" + - package-ecosystem: cargo + directory: userspace/ksuinit + schedule: + interval: daily + allow: + - dependency-type: "all" + groups: + crates: + patterns: + - "*" - package-ecosystem: cargo directory: userspace/ksud_magic schedule: diff --git a/.github/workflows/build-manager-ci.yml b/.github/workflows/build-manager-ci.yml index 24f13fde..c3b2b26a 100644 --- a/.github/workflows/build-manager-ci.yml +++ b/.github/workflows/build-manager-ci.yml @@ -69,9 +69,22 @@ jobs: with: os: ${{ matrix.os }} - build-ksud: + build-ksuinit: needs: [check-cache, build-susfsd] if: needs.check-cache.outputs.cache-hit != 'true' + strategy: + matrix: + include: + - target: aarch64-linux-android + os: ubuntu-latest + uses: ./.github/workflows/ksuinit.yml + with: + target: ${{ matrix.target }} + os: ${{ matrix.os }} + + build-ksud: + needs: [check-cache, build-ksuinit] + if: needs.check-cache.outputs.cache-hit != 'true' strategy: matrix: include: @@ -97,6 +110,13 @@ jobs: name: susfsd-linux-android path: cached-artifacts/susfsd + - name: Download ksuinit artifacts + uses: actions/download-artifact@v4 + with: + pattern: ksuinit-aarch64-linux-android + path: cached-artifacts/ksuinit + merge-multiple: true + - name: Download ksud_overlayfs artifacts uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/build-manager-spoofed.yml b/.github/workflows/build-manager-spoofed.yml index c84a4eff..b1950e35 100644 --- a/.github/workflows/build-manager-spoofed.yml +++ b/.github/workflows/build-manager-spoofed.yml @@ -71,9 +71,22 @@ jobs: with: os: ${{ matrix.os }} - build-ksud: + build-ksuinit: needs: [check-cache, build-susfsd] if: needs.check-cache.outputs.cache-hit != 'true' + strategy: + matrix: + include: + - target: aarch64-linux-android + os: ubuntu-latest + uses: ./.github/workflows/ksuinit.yml + with: + target: ${{ matrix.target }} + os: ${{ matrix.os }} + + build-ksud: + needs: [check-cache, build-ksuinit] + if: needs.check-cache.outputs.cache-hit != 'true' strategy: matrix: include: @@ -99,6 +112,13 @@ jobs: name: susfsd-linux-android path: cached-artifacts/susfsd + - name: Download ksuinit artifacts + uses: actions/download-artifact@v4 + with: + pattern: ksuinit-aarch64-linux-android + path: cached-artifacts/ksuinit + merge-multiple: true + - name: Download ksud_overlayfs artifacts uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index c40a756f..6f120841 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -33,8 +33,20 @@ jobs: with: os: ${{ matrix.os }} - build-ksud: + build-ksuinit: needs: build-susfsd + strategy: + matrix: + include: + - target: aarch64-linux-android + os: ubuntu-latest + uses: ./.github/workflows/ksuinit.yml + with: + target: ${{ matrix.target }} + os: ${{ matrix.os }} + + build-ksud: + needs: build-ksuinit strategy: matrix: include: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 234d4886..d41296b5 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -6,6 +6,7 @@ on: - next paths: - '.github/workflows/clippy.yml' + - 'userspace/ksuinit/**' - 'userspace/ksud_magic/**' - 'userspace/ksud_overlayfs/**' pull_request: @@ -13,6 +14,7 @@ on: - next paths: - '.github/workflows/clippy.yml' + - 'userspace/ksuinit/**' - 'userspace/ksud_magic/**' - 'userspace/ksud_overlayfs/**' @@ -32,6 +34,11 @@ jobs: - name: Setup Cross run: RUSTFLAGS="" cargo install cross + - name: Cache ksuinit + uses: Swatinem/rust-cache@v2 + with: + workspaces: userspace/ksuinit + - name: Cache ksud_overlayfs uses: Swatinem/rust-cache@v2 with: @@ -44,6 +51,8 @@ jobs: - name: Run Clippy run: | + cross clippy --manifest-path userspace/ksuinit/Cargo.toml --target aarch64-linux-android --release + cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target aarch64-linux-android --release cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target aarch64-linux-android --release diff --git a/.github/workflows/ksud.yml b/.github/workflows/ksud.yml index c4ab0c27..b5779a93 100644 --- a/.github/workflows/ksud.yml +++ b/.github/workflows/ksud.yml @@ -39,16 +39,17 @@ jobs: run: | cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_overlayfs/bin/aarch64/ cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_magic/bin/aarch64/ - cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_overlayfs/bin/arm/ - cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_magic/bin/arm/ - cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_overlayfs/bin/x86_64/ - cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_magic/bin/x86_64/ + + - name: Import ksuinit Binaries + run: | + cp ksuinit-aarch64-linux-android/aarch64-linux-android/release/ksuinit ./userspace/ksud_overlayfs/bin/aarch64/ + cp ksuinit-aarch64-linux-android/aarch64-linux-android/release/ksuinit ./userspace/ksud_magic/bin/aarch64/ - name: Setup Rust run: | rustup update stable - rustup target add x86_64-apple-darwin - rustup target add aarch64-apple-darwin + rustup target add x86_64-linux-android + rustup target add aarch64-linux-android - name: Cache ksud_overlayfs uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/ksuinit.yml b/.github/workflows/ksuinit.yml new file mode 100644 index 00000000..e7703014 --- /dev/null +++ b/.github/workflows/ksuinit.yml @@ -0,0 +1,57 @@ +name: Build ksuinit +on: + workflow_call: + inputs: + target: + required: true + type: string + os: + required: false + type: string + default: ubuntu-latest + pack_lkm: + required: false + type: boolean + default: true + use_cache: + required: false + type: boolean + default: true +jobs: + build: + runs-on: ${{ inputs.os }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Rust + run: | + rustup update stable + rustup target add aarch64-linux-android + + - name: Set Rust & Android linker + run: | + TOOLCHAIN="$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64" + if [ ! -d "$TOOLCHAIN" ]; then + echo "Android NDK not found at $ANDROID_NDK" + exit 1 + fi + echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$TOOLCHAIN/bin/aarch64-linux-android21-clang" >> $GITHUB_ENV + + - name: Cache ksuinit + uses: Swatinem/rust-cache@v2 + with: + workspaces: userspace/ksuinit + cache-targets: false + + - name: Build ksuinit + run: | + cargo build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksuinit/Cargo.toml + + - name: Upload ksuinit artifact + uses: actions/upload-artifact@v4 + with: + name: ksuinit-${{ inputs.target }} + path: userspace/ksuinit/target/**/release/ksuinit diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index c24a157c..4be053fc 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -6,6 +6,7 @@ on: - 'next' paths: - '.github/workflows/rustfmt.yml' + - 'userspace/ksuinit/**' - 'userspace/ksud_magic/**' - 'userspace/ksud_overlayfs/**' pull_request: @@ -13,6 +14,7 @@ on: - 'next' paths: - '.github/workflows/rustfmt.yml' + - 'userspace/ksuinit/**' - 'userspace/ksud_magic/**' - 'userspace/ksud_overlayfs/**' @@ -29,6 +31,11 @@ jobs: with: components: rustfmt + - uses: LoliGothick/rustfmt-check@master + with: + token: ${{ github.token }} + working-directory: userspace/ksuinit + - uses: LoliGothick/rustfmt-check@master with: token: ${{ github.token }} diff --git a/build.sh b/build.sh index 68a2a3c5..35d97ea2 100755 --- a/build.sh +++ b/build.sh @@ -6,6 +6,14 @@ # For LKM make sure you have imported the androidX-X.X_kernelsu.ko drivers to userspace/ksud_*/bin/aarch64 directory. +export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android21-clang" + +cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksuinit/Cargo.toml + +cp userspace/ksuinit/target/aarch64-linux-android/release/ksuinit userspace/ksud_magic/bin/aarch64/ksuinit + +cp userspace/ksuinit/target/aarch64-linux-android/release/ksuinit userspace/ksud_overlayfs/bin/aarch64/ksuinit + cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud_magic/Cargo.toml cp userspace/ksud_magic/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt index e196aab9..6014ebfc 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt @@ -81,35 +81,6 @@ import com.rifsxd.ksunext.ui.util.rootAvailable @Destination @Composable fun InstallScreen(navigator: DestinationsNavigator) { - var showLkmWarning by rememberSaveable { mutableStateOf(true) } - - if (showLkmWarning) { - AlertDialog( - onDismissRequest = { - showLkmWarning = false - navigator.popBackStack() - }, - title = { Text( - text = stringResource(R.string.warning), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.lkm_warning_message)) }, - confirmButton = { - TextButton(onClick = { showLkmWarning = false }) { - Text(stringResource(R.string.proceed)) - } - }, - dismissButton = { - TextButton(onClick = { - showLkmWarning = false - navigator.popBackStack() - }) { - Text(stringResource(R.string.cancel)) - } - } - ) - } var installMethod by remember { mutableStateOf(null) diff --git a/userspace/ksud_magic/bin/.gitignore b/userspace/ksud_magic/bin/.gitignore index c8b95e0e..d4e62ff8 100644 --- a/userspace/ksud_magic/bin/.gitignore +++ b/userspace/ksud_magic/bin/.gitignore @@ -1,2 +1,3 @@ **/*.ko -susfsd \ No newline at end of file +susfsd +ksuinit \ No newline at end of file diff --git a/userspace/ksud_overlayfs/bin/.gitignore b/userspace/ksud_overlayfs/bin/.gitignore index c8b95e0e..d4e62ff8 100644 --- a/userspace/ksud_overlayfs/bin/.gitignore +++ b/userspace/ksud_overlayfs/bin/.gitignore @@ -1,2 +1,3 @@ **/*.ko -susfsd \ No newline at end of file +susfsd +ksuinit \ No newline at end of file diff --git a/userspace/ksuinit/.gitignore b/userspace/ksuinit/.gitignore new file mode 100644 index 00000000..1de56593 --- /dev/null +++ b/userspace/ksuinit/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/userspace/ksuinit/Cargo.lock b/userspace/ksuinit/Cargo.lock new file mode 100644 index 00000000..edbc33d3 --- /dev/null +++ b/userspace/ksuinit/Cargo.lock @@ -0,0 +1,227 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "goblin" +version = "0.8.0" +source = "git+https://github.com/tiann/goblin#138d40d4c36471cfbb611eb493f0378ee9fc63f5" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "kernlog" +version = "0.3.1" +source = "git+https://github.com/tiann/kernlog.rs#ff0b1bd6d5261eae0fa297cec6951fe8d982151a" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "ksuinit" +version = "0.1.0" +dependencies = [ + "anyhow", + "goblin", + "kernlog", + "log", + "obfstr", + "rustix", + "scroll", + "syscalls", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "obfstr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2979b86cc910a6d13837ef97fef0c6b68fa807c5e014d622449db18351dc" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "git+https://github.com/Kernel-SU/rustix.git?rev=4a53fbc#4a53fbc7cb7a07cabe87125cc21dbc27db316259" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syscalls" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56b389b38331a454883a34fd19f25cbd1510b3510ff7aa28cb8d6de85d888439" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/userspace/ksuinit/Cargo.toml b/userspace/ksuinit/Cargo.toml new file mode 100644 index 00000000..35e8cb52 --- /dev/null +++ b/userspace/ksuinit/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ksuinit" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# for elf parsing +goblin = { git = "https://github.com/tiann/goblin" } +scroll = "0.12" + +anyhow = "1" +# If you want to use the following dependencies, please use aarch64-unknown-linux-musl & x86_64-unknown-linux-musl to compile statically +# rustix = { git = "https://github.com/bytecodealliance/rustix", rev = "7b44528", features = ["mount", "fs", "runtime", "system", "process"] } +rustix = { git = "https://github.com/Kernel-SU/rustix.git", rev = "4a53fbc", features = ["mount", "fs", "runtime", "system", "process"] } + +syscalls = { version = "0.6", default-features = false, features = [ + "aarch64", + "x86_64", +] } + +# for kmsg logging +log = "0.4" +kernlog = { git = "https://github.com/tiann/kernlog.rs" } + +obfstr = "0.4" + +[profile.release] +strip = true +lto = true +opt-level = "z" +panic = "abort" diff --git a/userspace/ksuinit/src/init.rs b/userspace/ksuinit/src/init.rs new file mode 100644 index 00000000..354ef708 --- /dev/null +++ b/userspace/ksuinit/src/init.rs @@ -0,0 +1,185 @@ +use std::io::{ErrorKind, Write}; + +use crate::loader::load_module; +use anyhow::Result; +use rustix::fs::{chmodat, symlink, unlink, AtFlags, Mode}; +use rustix::{ + fd::AsFd, + fs::{access, makedev, mkdir, mknodat, Access, FileType, CWD}, + mount::{ + fsconfig_create, fsmount, fsopen, move_mount, unmount, FsMountFlags, FsOpenFlags, + MountAttrFlags, MoveMountFlags, UnmountFlags, + }, +}; + +use obfstr::obfstr as s; + +struct AutoUmount { + mountpoints: Vec, +} + +impl Drop for AutoUmount { + fn drop(&mut self) { + for mountpoint in self.mountpoints.iter().rev() { + if let Err(e) = unmount(mountpoint.as_str(), UnmountFlags::DETACH) { + log::error!("{} {}: {}", s!("Cannot umount"), mountpoint, e) + } + } + } +} + +fn prepare_mount() -> AutoUmount { + let mut mountpoints = vec![]; + + // mount procfs + let result = mkdir("/proc", Mode::from_raw_mode(0o755)) + .or_else(|err| match err.kind() { + ErrorKind::AlreadyExists => Ok(()), + _ => Err(err), + }) + .and_then(|_| fsopen("proc", FsOpenFlags::FSOPEN_CLOEXEC)) + .and_then(|fd| fsconfig_create(fd.as_fd()).map(|_| fd)) + .and_then(|fd| { + fsmount( + fd.as_fd(), + FsMountFlags::FSMOUNT_CLOEXEC, + MountAttrFlags::empty(), + ) + }) + .and_then(|fd| { + move_mount( + fd.as_fd(), + "", + CWD, + "/proc", + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + ) + }); + match result { + Ok(_) => mountpoints.push("/proc".to_string()), + Err(e) => log::error!("{} {:?}", s!("Cannot mount procfs: "), e), + } + + // mount sysfs + let result = mkdir("/sys", Mode::from_raw_mode(0o755)) + .or_else(|err| match err.kind() { + ErrorKind::AlreadyExists => Ok(()), + _ => Err(err), + }) + .and_then(|_| fsopen("sysfs", FsOpenFlags::FSOPEN_CLOEXEC)) + .and_then(|fd| fsconfig_create(fd.as_fd()).map(|_| fd)) + .and_then(|fd| { + fsmount( + fd.as_fd(), + FsMountFlags::FSMOUNT_CLOEXEC, + MountAttrFlags::empty(), + ) + }) + .and_then(|fd| { + move_mount( + fd.as_fd(), + "", + CWD, + "/sys", + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + ) + }); + + match result { + Ok(_) => mountpoints.push("/sys".to_string()), + Err(e) => log::error!("{} {:?}", s!("Cannot mount sysfs:"), e), + } + + AutoUmount { mountpoints } +} + +fn setup_kmsg() { + const KMSG: &str = "/dev/kmsg"; + let device = match access(KMSG, Access::EXISTS) { + Ok(_) => KMSG, + Err(_) => { + // try to create it + mknodat( + CWD, + "/kmsg", + FileType::CharacterDevice, + 0o666.into(), + makedev(1, 11), + ) + .ok(); + "/kmsg" + } + }; + + let _ = kernlog::init_with_device(device); +} + +fn unlimit_kmsg() { + // Disable kmsg rate limiting + if let Ok(mut rate) = std::fs::File::options() + .write(true) + .open(s!("/proc/sys/kernel/printk_devkmsg")) + { + writeln!(rate, "on").ok(); + } +} + +pub fn init() -> Result<()> { + // Setup kernel log first + setup_kmsg(); + + log::info!("{}", s!("Hello, KernelSU!")); + + // mount /proc and /sys to access kernel interface + let _dontdrop = prepare_mount(); + + // This relies on the fact that we have /proc mounted + unlimit_kmsg(); + + if has_kernelsu() { + log::info!("{}", s!("KernelSU may be already loaded in kernel, skip!")); + } else { + log::info!("{}", s!("Loading kernelsu.ko..")); + if let Err(e) = load_module(s!("/kernelsu.ko")) { + log::error!("{}: {}", s!("Cannot load kernelsu.ko"), e); + } + } + + // And now we should prepare the real init to transfer control to it + unlink("/init")?; + + let real_init = match access("/init.real", Access::EXISTS) { + Ok(_) => "init.real", + Err(_) => "/system/bin/init", + }; + + log::info!("{} {}", s!("init is"), real_init); + symlink(real_init, "/init")?; + + chmodat( + CWD, + "/init", + Mode::from_raw_mode(0o755), + AtFlags::SYMLINK_NOFOLLOW, + )?; + + Ok(()) +} + +fn has_kernelsu() -> bool { + use syscalls::{syscall, Sysno}; + let mut version = 0; + const CMD_GET_VERSION: i32 = 2; + unsafe { + let _ = syscall!( + Sysno::prctl, + 0xDEADBEEF, + CMD_GET_VERSION, + std::ptr::addr_of_mut!(version) + ); + } + + log::info!("{}: {}", s!("KernelSU version"), version); + + version != 0 +} diff --git a/userspace/ksuinit/src/loader.rs b/userspace/ksuinit/src/loader.rs new file mode 100644 index 00000000..ea4ece90 --- /dev/null +++ b/userspace/ksuinit/src/loader.rs @@ -0,0 +1,97 @@ +use anyhow::{Context, Result}; +use goblin::elf::{section_header, sym::Sym, Elf}; +use rustix::{cstr, system::init_module}; +use scroll::{ctx::SizeWith, Pwrite}; +use std::collections::HashMap; +use std::fs; + +use obfstr::obfstr as s; + +struct Kptr { + value: String, +} + +impl Kptr { + pub fn new() -> Result { + let value = fs::read_to_string(s!("/proc/sys/kernel/kptr_restrict"))?; + fs::write(s!("/proc/sys/kernel/kptr_restrict"), "1")?; + Ok(Kptr { value }) + } +} + +impl Drop for Kptr { + fn drop(&mut self) { + let _ = fs::write(s!("/proc/sys/kernel/kptr_restrict"), self.value.as_bytes()); + } +} + +fn parse_kallsyms() -> Result> { + let _dontdrop = Kptr::new()?; + + let allsyms = fs::read_to_string(s!("/proc/kallsyms"))? + .lines() + .map(|line| line.split_whitespace()) + .filter_map(|mut splits| { + splits + .next() + .and_then(|addr| u64::from_str_radix(addr, 16).ok()) + .and_then(|addr| splits.nth(1).map(|symbol| (symbol, addr))) + }) + .map(|(symbol, addr)| { + ( + symbol + .find("$").or_else(|| symbol.find(".llvm.")) + .map_or(symbol, |pos| &symbol[0..pos]) + .to_owned(), + addr, + ) + }) + .collect::>(); + + Ok(allsyms) +} + +pub fn load_module(path: &str) -> Result<()> { + // check if self is init process(pid == 1) + if !rustix::process::getpid().is_init() { + anyhow::bail!("{}", s!("Invalid process")); + } + + let mut buffer = + fs::read(path).with_context(|| format!("{} {}", s!("Cannot read file"), path))?; + let elf = Elf::parse(&buffer)?; + + let kernel_symbols = + parse_kallsyms().with_context(|| s!("Cannot parse kallsyms").to_string())?; + + let mut modifications = Vec::new(); + for (index, mut sym) in elf.syms.iter().enumerate() { + if index == 0 { + continue; + } + + if sym.st_shndx != section_header::SHN_UNDEF as usize { + continue; + } + + let Some(name) = elf.strtab.get_at(sym.st_name) else { + continue; + }; + + let offset = elf.syms.offset() + index * Sym::size_with(elf.syms.ctx()); + let Some(real_addr) = kernel_symbols.get(name) else { + log::warn!("{}: {}", s!("Cannot found symbol"), &name); + continue; + }; + sym.st_shndx = section_header::SHN_ABS as usize; + sym.st_value = *real_addr; + modifications.push((sym, offset)); + } + + let ctx = *elf.syms.ctx(); + for ele in modifications { + buffer.pwrite_with(ele.0, ele.1, ctx)?; + } + init_module(&buffer, cstr!("")).with_context(|| s!("init_module failed.").to_string())?; + Ok(()) +} diff --git a/userspace/ksuinit/src/main.rs b/userspace/ksuinit/src/main.rs new file mode 100644 index 00000000..a6aa4a91 --- /dev/null +++ b/userspace/ksuinit/src/main.rs @@ -0,0 +1,19 @@ +#![no_main] + +mod init; +mod loader; + +use rustix::{cstr, runtime::execve}; +/// # Safety +/// This is the entry point of the program +/// We cannot use the main because rust will abort if we don't have std{in/out/err} +/// https://github.com/rust-lang/rust/blob/3071aefdb2821439e2e6f592f41a4d28e40c1e79/library/std/src/sys/unix/mod.rs#L80 +/// So we use the C main function and call rust code from there +#[no_mangle] +pub unsafe extern "C" fn main(_argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32 { + let _ = init::init(); + unsafe { + execve(cstr!("/init"), argv, envp); + } + 0 +}