diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e346710 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.gradle +.idea +.cxx +build +local.properties diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..5983a22 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,50 @@ +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.gradle.BaseExtension + +plugins { + id("com.android.application") apply false + id("com.android.library") apply false +} + +val verCode by extra(25207) +val verName by extra("25.2-1") +val androidMinSdkVersion by extra(29) +val androidTargetSdkVersion by extra(33) +val androidCompileSdkVersion by extra(33) +val androidBuildToolsVersion by extra("33.0.1") +val androidCompileNdkVersion by extra("25.1.8937393") +val androidSourceCompatibility by extra(JavaVersion.VERSION_11) +val androidTargetCompatibility by extra(JavaVersion.VERSION_11) + +tasks.register("Delete", Delete::class) { + delete(rootProject.buildDir) +} + +fun Project.configureBaseExtension() { + extensions.findByType(BaseExtension::class)?.run { + compileSdkVersion(androidCompileSdkVersion) + ndkVersion = androidCompileNdkVersion + buildToolsVersion = androidBuildToolsVersion + + defaultConfig { + minSdk = androidMinSdkVersion + targetSdk = androidTargetSdkVersion + versionCode = verCode + versionName = verName + } + } + + extensions.findByType(ApplicationExtension::class)?.lint { + abortOnError = true + checkReleaseBuilds = false + } +} + +subprojects { + plugins.withId("com.android.application") { + configureBaseExtension() + } + plugins.withId("com.android.library") { + configureBaseExtension() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..f26fd69 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/module/build.gradle.kts b/module/build.gradle.kts new file mode 100644 index 0000000..1639a6c --- /dev/null +++ b/module/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("com.android.application") +} + +val moduleName = "Zygisk On KernelSU" +val moduleBaseId = "zygisksu" +val authors = "Nullptr" + +val verCode: Int by rootProject.extra +val verName: String by rootProject.extra + +android { + namespace = "icu.nullptr.zygisksu" +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..a808014 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,27 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + plugins { + id("com.android.library") version "7.4.0" + id("com.android.application") version "7.4.0" + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "ZygiskOnKernelSU" +include( + ":module", + ":zygiskd", +) diff --git a/zygiskd/Cargo.lock b/zygiskd/Cargo.lock new file mode 100644 index 0000000..1f2ec0a --- /dev/null +++ b/zygiskd/Cargo.lock @@ -0,0 +1,494 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037f3e1da32ddba7770530e69258b742c15ad67bdf90e5f6b35f4b6db9a60eb7" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_format" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", + "static_assertions", +] + +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[package]] +name = "num_enum" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "passfd" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40937dff86b281e4025fe0614c744d961797552ccf8dc681a297e694db283ef3" +dependencies = [ + "libc", +] + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-crate" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +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.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "zygiskd" +version = "1.0.0" +dependencies = [ + "android_logger", + "anyhow", + "const_format", + "env_logger", + "log", + "memfd", + "nix", + "num_enum", + "passfd", +] diff --git a/zygiskd/Cargo.toml b/zygiskd/Cargo.toml new file mode 100644 index 0000000..d53a16e --- /dev/null +++ b/zygiskd/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "zygiskd" +version = "1.0.0" +edition = "2021" +rust-version = "1.65" + +[dependencies] +android_logger = "0.12.0" +anyhow = "1.0.68" +const_format = "0.2.5" +env_logger = "0.10.0" +log = "0.4.17" +memfd = "0.6.2" +nix = "0.26.2" +num_enum = "0.5.9" +passfd = "0.1.5" + +[profile.release] +strip = true +opt-level = "z" +lto = true diff --git a/zygiskd/build.gradle.kts b/zygiskd/build.gradle.kts new file mode 100644 index 0000000..b8cb158 --- /dev/null +++ b/zygiskd/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("com.android.library") +} + +val verCode: Int by rootProject.extra +val verName: String by rootProject.extra + +android { + namespace = "icu.nullptr.zygisksu" +} diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs new file mode 100644 index 0000000..55ca36e --- /dev/null +++ b/zygiskd/src/constants.rs @@ -0,0 +1,19 @@ +use const_format::concatcp; +use num_enum::TryFromPrimitive; + +pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge"; + +pub const KSU_MODULE_DIR: &str = "/data/adb/ksu/modules"; +pub const ZYGISKSU_DIR: &str = concatcp!(KSU_MODULE_DIR, "/zygisksu"); +pub const ZYGISKWD: &str = concatcp!(ZYGISKSU_DIR, "/zygiskwd"); +pub const ZYGISKD32: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd32"); +pub const ZYGISKD64: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd64"); +pub const DAEMON_LOCK: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd.lock"); + +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +#[repr(u8)] +pub enum DaemonSocketAction { + ReadNativeBridge, + ReadModules, + RequestCompanionSocket, +} diff --git a/zygiskd/src/main.rs b/zygiskd/src/main.rs new file mode 100644 index 0000000..eb0641e --- /dev/null +++ b/zygiskd/src/main.rs @@ -0,0 +1,48 @@ +#![allow(dead_code)] + +mod watchdog; +mod constants; +mod zygisk; +mod utils; + +use anyhow::{anyhow, Result}; +use log::LevelFilter; +use nix::libc; + +fn init_android_logger(tag: &str) { + android_logger::init_once( + android_logger::Config::default() + .with_max_level(LevelFilter::Trace) + .with_tag(tag), + ); +} + +fn main() -> Result<()> { + let process = std::env::args().next().unwrap(); + let process = process.split('/').last().unwrap(); + env_logger::init(); + // init_android_logger(process); + match process { + "zygiskwd" => { + log::info!("Start zygisksu watchdog"); + // watchdog::check_permission()?; + watchdog::ensure_single_instance()?; + watchdog::spawn_daemon()?; + } + "zygiskd32" => { + log::info!("Start zygiskd32"); + unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); } + zygisk::start(false)?; + loop {} + } + "zygiskd64" => { + log::info!("Start zygiskd64"); + unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); } + zygisk::start(true)?; + } + _ => return { + Err(anyhow!("Unexpected process name: {process}")) + }, + } + Ok(()) +} diff --git a/zygiskd/src/utils.rs b/zygiskd/src/utils.rs new file mode 100644 index 0000000..c212a97 --- /dev/null +++ b/zygiskd/src/utils.rs @@ -0,0 +1,58 @@ +use crate::constants; +use anyhow::Result; +use nix::unistd::gettid; +use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command}; + +pub fn set_socket_create_context(context: &str) -> Result<()> { + let path = "/proc/thread-self/attr/sockcreate"; + match fs::write(path, context) { + Ok(_) => Ok(()), + Err(_) => { + let path = format!("/proc/self/task/{}/attr/sockcreate", gettid().as_raw()); + fs::write(path, context)?; + Ok(()) + } + } +} + +pub fn get_native_bridge() -> String { + std::env::var("NATIVE_BRIDGE").unwrap_or("0".to_string()) +} + +pub fn restore_native_bridge() -> Result<()> { + let exec = format!("resetprop {} {}", constants::PROP_NATIVE_BRIDGE, get_native_bridge()); + Command::new(exec).spawn()?.wait()?; + Ok(()) +} + +pub trait UnixStreamExt { + fn read_u8(&mut self) -> Result; + fn read_u32(&mut self) -> Result; + fn read_usize(&mut self) -> Result; + fn write_usize(&mut self, value: usize) -> Result<()>; +} + +impl UnixStreamExt for UnixStream { + fn read_u8(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.read_exact(&mut buf)?; + Ok(buf[0]) + } + + fn read_u32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + + fn read_usize(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(usize::from_le_bytes(buf)) + } + + fn write_usize(&mut self, value: usize) -> Result<()> { + self.write_all(&value.to_ne_bytes())?; + Ok(()) + } +} diff --git a/zygiskd/src/watchdog.rs b/zygiskd/src/watchdog.rs new file mode 100644 index 0000000..125bb7f --- /dev/null +++ b/zygiskd/src/watchdog.rs @@ -0,0 +1,70 @@ +use crate::constants; +use anyhow::{anyhow, Result}; +use nix::fcntl::{flock, FlockArg}; +use nix::unistd::{getgid, getuid}; +use std::os::unix::prelude::AsRawFd; +use std::process::{Child, Command}; +use std::sync::mpsc; +use std::{fs, thread}; + +static mut LOCK_FILE: Option = None; + +pub fn check_permission() -> Result<()> { + log::info!("Check permission"); + let uid = getuid(); + if uid.as_raw() != 0 { + return Err(anyhow!("UID is not 0")); + } + + let gid = getgid(); + if gid.as_raw() != 0 { + return Err(anyhow!("GID is not 0")); + } + + let context = fs::read_to_string("/proc/self/attr/current")?; + if context != "u:r:su:s0" { + return Err(anyhow!("SELinux context is not u:r:su:s0")); + } + + Ok(()) +} + +pub fn ensure_single_instance() -> Result<()> { + log::info!("Ensure single instance"); + let metadata = fs::metadata(constants::ZYGISKSU_DIR); + if metadata.is_err() || !metadata.unwrap().is_dir() { + return Err(anyhow!("Zygisksu is not installed")); + } + unsafe { + match fs::File::create(constants::DAEMON_LOCK) { + Ok(file) => LOCK_FILE = Some(file), + Err(e) => return Err(anyhow!("Failed to open lock file: {e}")), + }; + let fd = LOCK_FILE.as_ref().unwrap().as_raw_fd(); + if let Err(e) = flock(fd, FlockArg::LockExclusiveNonblock) { + return Err(anyhow!( + "Failed to acquire lock: {e}. Maybe another instance is running?" + )); + } + } + Ok(()) +} + +pub fn spawn_daemon() -> Result<()> { + let daemon32 = Command::new(constants::ZYGISKD32).spawn()?; + let daemon64 = Command::new(constants::ZYGISKD64).spawn()?; + let (sender, receiver) = mpsc::channel(); + let spawn = |mut daemon: Child| { + let sender = sender.clone(); + thread::spawn(move || { + let result = daemon.wait().unwrap(); + log::error!("Daemon process {} died: {}", daemon.id(), result); + drop(daemon); + sender.send(()).unwrap(); + }); + }; + spawn(daemon32); + spawn(daemon64); + let _ = receiver.recv(); + Err(anyhow!("Daemon process died")) +} diff --git a/zygiskd/src/zygisk.rs b/zygiskd/src/zygisk.rs new file mode 100644 index 0000000..f7978ce --- /dev/null +++ b/zygiskd/src/zygisk.rs @@ -0,0 +1,240 @@ +use crate::constants::DaemonSocketAction; +use crate::utils::{restore_native_bridge, UnixStreamExt}; +use crate::{constants, utils}; +use anyhow::{anyhow, Result}; +use memfd::Memfd; +use nix::{ + libc::{self, dlsym}, + unistd::getppid, +}; +use passfd::FdPassingExt; +use std::cell::Cell; +use std::io::Write; +use std::sync::Arc; +use std::thread; +use std::{ + ffi::c_void, + fs, + os::unix::{ + net::{UnixListener, UnixStream}, + prelude::AsRawFd, + }, + path::PathBuf, +}; + +type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32); + +struct Module { + name: String, + memfd: Memfd, + companion_entry: Option, +} + +struct Context { + native_bridge: String, + modules: Arc>, + stream: Cell, +} + +pub fn start(is64: bool) -> Result<()> { + // check_parent()?; + let arch = get_arch(is64)?; + log::debug!("Daemon architecture: {arch}"); + + log::info!("Load modules"); + let modules = load_modules(arch)?; + + log::info!("Create socket"); + let listener = create_daemon_socket(is64)?; + + log::info!("Waiting for connection"); + let (stream, _) = listener.accept()?; + drop(listener); + + let context = Context { + native_bridge: utils::get_native_bridge(), + modules: Arc::new(modules), + stream: Cell::new(stream), + }; + + log::info!("Connection established"); + restore_native_bridge()?; + handle_daemon_actions(context)?; + + Ok(()) +} + +fn check_parent() -> Result<()> { + let parent = fs::read_link(format!("/proc/{}/exe", getppid().as_raw()))?; + let parent = parent.file_name().unwrap().to_str().unwrap(); + if parent != constants::ZYGISKWD { + return Err(anyhow!("zygiskd is not started by watchdog")); + } + Ok(()) +} + +fn get_arch(is64: bool) -> Result<&'static str> { + // let output = Command::new("getprop ro.product.cpu.abi").output()?; + // let system_arch = String::from_utf8(output.stdout)?; + let system_arch = "x86_64"; // DEBUGGING + let is_arm = system_arch.contains("arm"); + let is_x86 = system_arch.contains("x86"); + if is64 { + match (is_arm, is_x86) { + (true, _) => Ok("arm64-v8a"), + (_, true) => Ok("x86_64"), + _ => Err(anyhow!("Unsupported system architecture: {}", system_arch)), + } + } else { + match (is_arm, is_x86) { + (true, _) => Ok("armeabi-v7a"), + (_, true) => Ok("x86"), + _ => Err(anyhow!("Unsupported system architecture: {}", system_arch)), + } + } +} + +fn load_modules(arch: &str) -> Result> { + let mut modules = Vec::new(); + let dir = match fs::read_dir(constants::KSU_MODULE_DIR) { + Ok(dir) => dir, + Err(e) => { + log::warn!("Failed reading modules directory: {}", e); + return Ok(modules); + } + }; + for entry_result in dir.into_iter() { + let entry = entry_result?; + let name = entry.file_name().into_string().unwrap(); + let so_path = entry.path().join(format!("zygisk/{arch}.so")); + if !so_path.exists() { + continue; + } + log::info!(" Loading module `{name}`..."); + let memfd = match create_memfd(&name, &so_path) { + Ok(memfd) => memfd, + Err(e) => { + log::warn!(" Failed to create memfd for `{name}`: {e}"); + continue; + } + }; + let companion_entry = match preload_module(&memfd) { + Ok(companion_entry) => companion_entry, + Err(e) => { + log::warn!(" Failed to preload `{name}`: {e}"); + continue; + } + }; + let module = Module { + name, + memfd, + companion_entry, + }; + modules.push(module); + } + + Ok(modules) +} + +fn create_memfd(name: &str, so_path: &PathBuf) -> Result { + let opts = memfd::MemfdOptions::default().allow_sealing(true); + let memfd = opts.create(name)?; + + let file = fs::File::open(so_path)?; + let mut reader = std::io::BufReader::new(file); + let mut writer = memfd.as_file(); + std::io::copy(&mut reader, &mut writer)?; + + let mut seals = memfd::SealsHashSet::new(); + seals.insert(memfd::FileSeal::SealShrink); + seals.insert(memfd::FileSeal::SealGrow); + seals.insert(memfd::FileSeal::SealWrite); + seals.insert(memfd::FileSeal::SealSeal); + memfd.add_seals(&seals)?; + + Ok(memfd) +} + +fn preload_module(memfd: &Memfd) -> Result> { + unsafe { + let path = format!("/proc/self/fd/{}", memfd.as_raw_fd()); + let filename = std::ffi::CString::new(path)?; + let handle = libc::dlopen(filename.as_ptr(), libc::RTLD_LAZY); + if handle.is_null() { + let e = std::ffi::CStr::from_ptr(libc::dlerror()) + .to_string_lossy() + .into_owned(); + return Err(anyhow!("dlopen failed: {}", e)); + } + let symbol = std::ffi::CString::new("zygisk_companion_entry")?; + let entry = dlsym(handle, symbol.as_ptr()); + if entry.is_null() { + return Ok(None); + } + let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry); + Ok(Some(fnptr)) + } +} + +fn create_daemon_socket(is64: bool) -> Result { + utils::set_socket_create_context("u:r:zygote:s0")?; + let socket_name = if is64 { "zygiskd64" } else { "zygiskd32" }; + let listener = UnixListener::bind(socket_name)?; + Ok(listener) +} + +fn handle_daemon_actions(mut context: Context) -> Result<()> { + let stream = context.stream.get_mut(); + loop { + let action = stream.read_u8()?; + match DaemonSocketAction::try_from(action) { + Ok(DaemonSocketAction::ReadNativeBridge) => { + stream.write_usize(context.native_bridge.len())?; + stream.write_all(context.native_bridge.as_bytes())?; + } + Ok(DaemonSocketAction::ReadModules) => { + stream.write_usize(context.modules.len())?; + for module in context.modules.iter() { + stream.write_usize(module.name.len())?; + stream.write_all(module.name.as_bytes())?; + stream.send_fd(module.memfd.as_raw_fd())?; + } + } + Ok(DaemonSocketAction::RequestCompanionSocket) => { + let (server, client) = UnixStream::pair()?; + stream.send_fd(client.as_raw_fd())?; + let modules_ref = Arc::clone(&context.modules); + thread::spawn(move || { + if let Err(e) = create_companion(server, modules_ref.as_ref()) { + log::warn!("Companion thread exited: {e}"); + } + }); + } + Err(_) => { + return Err(anyhow!("Invalid action code: {action}")); + } + } + } +} + +fn create_companion(mut server: UnixStream, modules: &Vec) -> Result<()> { + loop { + let index = match server.read_usize() { + Ok(index) => index, + Err(_) => return Ok(()), // EOF + }; + let module = &modules[index]; + log::debug!("New companion request from module {}", module.name); + + unsafe { + match module.companion_entry { + Some(entry) => { + let (sock_app, sock_companion) = UnixStream::pair()?; + server.send_fd(sock_app.as_raw_fd())?; + entry(sock_companion.as_raw_fd()); + } + None => (), + } + } + } +}