├── .gitignore ├── .gitmodules ├── README.md ├── build.gradle ├── build.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── magisk ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script ├── config ├── customize.sh ├── module.prop ├── service.sh └── system.prop ├── settings.gradle └── zygisk ├── .gitignore ├── build.gradle ├── jni ├── Android.mk ├── Application.mk ├── android.cpp ├── android.h ├── config.cpp ├── config.h ├── external │ └── Android.mk ├── hook.cpp ├── hook.h ├── logging.h ├── module.cpp └── zygisk.hpp └── src └── main └── AndroidManifest.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | 12 | *.zip 13 | magisk/zygisk 14 | *.dex 15 | *.swp 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libcxx"] 2 | path = zygisk/jni/libcxx 3 | url = https://github.com/topjohnwu/libcxx.git 4 | [submodule "xhook"] 5 | path = module/jni/external/xhook 6 | url = https://github.com/iqiyi/xHook.git 7 | [submodule "module/jni/external/xhook"] 8 | path = zygisk/jni/external/xhook 9 | url = https://github.com/iqiyi/xHook.git 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## How to build 2 | ``` 3 | git submodule update --init --recursive 4 | bash build.sh 5 | ``` 6 | 7 | ## License 8 | 9 | Although the main Magisk project is licensed under GPLv3, the Zygisk API and its headers are not. Every file in this repository is released to the public domain, so you don't have to worry about any licensing issues while developing Zygisk modules. 10 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.0.3" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | task clean(type: Delete) { 16 | delete rootProject.buildDir 17 | } 18 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | build_mode="${1:-release}" 5 | 6 | cd "$(dirname "$0")" 7 | 8 | pushd zygisk 9 | rm -fr libs 10 | debug_mode=1 11 | if [[ "$build_mode" == "release" ]]; then 12 | debug_mode=0 13 | fi 14 | /opt/android_sdk/ndk/21.4.7075529/ndk-build -j48 NDK_DEBUG=$debug_mode 15 | popd 16 | 17 | mkdir -p magisk/zygisk 18 | for arch in arm64-v8a armeabi-v7a x86 x86_64 19 | do 20 | cp "zygisk/libs/$arch/libdevicefaker.so" "magisk/zygisk/$arch.so" 21 | done 22 | 23 | pushd magisk 24 | version="$(grep '^version=' module.prop | cut -d= -f2)" 25 | rm -f "../devicefaker-zygisk-$version.zip" 26 | zip -r9 "../devicefaker-zygisk-$version.zip" . 27 | popd 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroPlus-org/zygisk-devicefaker/5071135e1fd3cd34f670e69816a9dbd05821a07b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 26 02:13:18 PDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /magisk/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v19.0+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 19000 ] && require_new_magisk 31 | 32 | if [ $MAGISK_VER_CODE -ge 20400 ]; then 33 | # New Magisk have complete installation logic within util_functions.sh 34 | install_module 35 | exit 0 36 | fi 37 | 38 | ################# 39 | # Legacy Support 40 | ################# 41 | 42 | # Global vars 43 | TMPDIR=/dev/tmp 44 | PERSISTDIR=/sbin/.magisk/mirror/persist 45 | 46 | rm -rf $TMPDIR 2>/dev/null 47 | mkdir -p $TMPDIR 48 | 49 | is_legacy_script() { 50 | unzip -l "$ZIPFILE" install.sh | grep -q install.sh 51 | return $? 52 | } 53 | 54 | print_modname() { 55 | local len 56 | len=`echo -n $MODNAME | wc -c` 57 | len=$((len + 2)) 58 | local pounds=`printf "%${len}s" | tr ' ' '*'` 59 | ui_print "$pounds" 60 | ui_print " $MODNAME " 61 | ui_print "$pounds" 62 | ui_print "*******************" 63 | ui_print " Powered by Magisk " 64 | ui_print "*******************" 65 | } 66 | 67 | # Preperation for flashable zips 68 | setup_flashable 69 | 70 | # Mount partitions 71 | mount_partitions 72 | 73 | # Detect version and architecture 74 | api_level_arch_detect 75 | 76 | # Setup busybox and binaries 77 | $BOOTMODE && boot_actions || recovery_actions 78 | 79 | ############## 80 | # Preparation 81 | ############## 82 | 83 | # Extract prop file 84 | unzip -o "$ZIPFILE" module.prop -d $TMPDIR >&2 85 | [ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!" 86 | 87 | $BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules 88 | MODULEROOT=$NVBASE/$MODDIRNAME 89 | MODID=`grep_prop id $TMPDIR/module.prop` 90 | MODPATH=$MODULEROOT/$MODID 91 | MODNAME=`grep_prop name $TMPDIR/module.prop` 92 | 93 | # Create mod paths 94 | rm -rf $MODPATH 2>/dev/null 95 | mkdir -p $MODPATH 96 | 97 | ########## 98 | # Install 99 | ########## 100 | 101 | if is_legacy_script; then 102 | unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2 103 | 104 | # Load install script 105 | . $TMPDIR/install.sh 106 | 107 | # Callbacks 108 | print_modname 109 | on_install 110 | 111 | # Custom uninstaller 112 | [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh 113 | 114 | # Skip mount 115 | $SKIPMOUNT && touch $MODPATH/skip_mount 116 | 117 | # prop file 118 | $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop 119 | 120 | # Module info 121 | cp -af $TMPDIR/module.prop $MODPATH/module.prop 122 | 123 | # post-fs-data scripts 124 | $POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh 125 | 126 | # service scripts 127 | $LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh 128 | 129 | ui_print "- Setting permissions" 130 | set_permissions 131 | else 132 | print_modname 133 | 134 | unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2 135 | 136 | if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then 137 | ui_print "- Extracting module files" 138 | unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 139 | 140 | # Default permissions 141 | set_perm_recursive $MODPATH 0 0 0755 0644 142 | fi 143 | 144 | # Load customization script 145 | [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh 146 | fi 147 | 148 | # Handle replace folders 149 | for TARGET in $REPLACE; do 150 | ui_print "- Replace target: $TARGET" 151 | mktouch $MODPATH$TARGET/.replace 152 | done 153 | 154 | if $BOOTMODE; then 155 | # Update info for Magisk Manager 156 | mktouch $NVBASE/modules/$MODID/update 157 | cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop 158 | fi 159 | 160 | # Copy over custom sepolicy rules 161 | if [ -f $MODPATH/sepolicy.rule -a -e $PERSISTDIR ]; then 162 | ui_print "- Installing custom sepolicy patch" 163 | PERSISTMOD=$PERSISTDIR/magisk/$MODID 164 | mkdir -p $PERSISTMOD 165 | cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule 166 | fi 167 | 168 | # Remove stuffs that don't belong to modules 169 | rm -rf \ 170 | $MODPATH/system/placeholder $MODPATH/customize.sh \ 171 | $MODPATH/README.md $MODPATH/.git* 2>/dev/null 172 | 173 | ############## 174 | # Finalizing 175 | ############## 176 | 177 | cd / 178 | $BOOTMODE || recovery_cleanup 179 | rm -rf $TMPDIR 180 | 181 | ui_print "- Done" 182 | exit 0 183 | -------------------------------------------------------------------------------- /magisk/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /magisk/config: -------------------------------------------------------------------------------- 1 | com.epicgames.fortnite 2 | com.ea.gp.apexlegendsmobilefps 3 | ru.andr7e.deviceinfohw -------------------------------------------------------------------------------- /magisk/customize.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | # Android 8.0 or newer 4 | if [[ "$(getprop ro.build.version.sdk)" -lt 26 ]]; then 5 | ui_print "This module only supports Android 8.0 and newer." 6 | 7 | # Abort install and clean up 8 | rm -fr "$TMPDIR" "$MODPATH" 9 | exit 1 10 | fi 11 | 12 | chmod 755 "$MODPATH/service.sh" "$MODPATH/post-fs-data.sh" 13 | -------------------------------------------------------------------------------- /magisk/module.prop: -------------------------------------------------------------------------------- 1 | id=devicefaker-zygisk 2 | name=Zygisk Device Faker 3 | version=v01 4 | versionCode=1 5 | author=AndroPlus 6 | description=Zygisk module to disguise as if the device is Glaxy Tab S7 only for certain apps 7 | -------------------------------------------------------------------------------- /magisk/service.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | # Conditional MagiskHide properties 3 | 4 | ui_print "Execution complete!" 5 | -------------------------------------------------------------------------------- /magisk/system.prop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroPlus-org/zygisk-devicefaker/5071135e1fd3cd34f670e69816a9dbd05821a07b/magisk/system.prop -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | include ':module' 9 | -------------------------------------------------------------------------------- /zygisk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /libs 3 | /obj 4 | -------------------------------------------------------------------------------- /zygisk/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | externalNativeBuild { 7 | ndkBuild { 8 | path("jni/Android.mk") 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /zygisk/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := devicefaker 5 | LOCAL_SRC_FILES := module.cpp config.cpp hook.cpp android.cpp 6 | LOCAL_STATIC_LIBRARIES := libcxx libxhook 7 | LOCAL_LDLIBS := -llog 8 | include $(BUILD_SHARED_LIBRARY) 9 | 10 | include jni/libcxx/Android.mk 11 | include jni/external/Android.mk 12 | 13 | # If you do not want to use libc++, link to system stdc++ 14 | # so that you can at least call the new operator in your code 15 | 16 | # include $(CLEAR_VARS) 17 | # LOCAL_MODULE := example 18 | # LOCAL_SRC_FILES := example.cpp 19 | # LOCAL_LDLIBS := -llog -lstdc++ 20 | # include $(BUILD_SHARED_LIBRARY) 21 | -------------------------------------------------------------------------------- /zygisk/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 2 | APP_CPPFLAGS := -std=c++17 -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden 3 | APP_STL := none 4 | APP_PLATFORM := android-21 5 | -------------------------------------------------------------------------------- /zygisk/jni/android.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace android { 4 | 5 | static int apiLevel = 0; 6 | static int previewApiLevel = 0; 7 | 8 | int GetApiLevel() { 9 | if (apiLevel > 0) return apiLevel; 10 | 11 | char buf[PROP_VALUE_MAX + 1]; 12 | if (__system_property_get("ro.build.version.sdk", buf) > 0) 13 | apiLevel = atoi(buf); 14 | 15 | return apiLevel; 16 | } 17 | 18 | int GetPreviewApiLevel() { 19 | if (previewApiLevel > 0) return previewApiLevel; 20 | 21 | char buf[PROP_VALUE_MAX + 1]; 22 | if (__system_property_get("ro.build.version.preview_sdk", buf) > 0) 23 | previewApiLevel = atoi(buf); 24 | 25 | return previewApiLevel; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /zygisk/jni/android.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace android { 4 | 5 | int GetApiLevel(); 6 | 7 | int GetPreviewApiLevel(); 8 | } 9 | -------------------------------------------------------------------------------- /zygisk/jni/config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "logging.h" 10 | 11 | using namespace Config; 12 | 13 | namespace Config { 14 | 15 | namespace Properties { 16 | 17 | void Put(const char *name, const char *value); 18 | } 19 | 20 | namespace Packages { 21 | 22 | void Add(const char *name); 23 | } 24 | } 25 | 26 | 27 | static std::map props; 28 | static std::vector packages; 29 | 30 | Property *Properties::Find(const char *name) { 31 | if (!name) return nullptr; 32 | 33 | auto it = props.find(name); 34 | if (it != props.end()) { 35 | return it->second; 36 | } 37 | return nullptr; 38 | } 39 | 40 | void Properties::Put(const char *name, const char *value) { 41 | if (!name) return; 42 | 43 | auto prop = Find(name); 44 | delete prop; 45 | 46 | props[name] = new Property(name, value ? value : ""); 47 | 48 | LOGD("property: %s %s", name, value); 49 | } 50 | 51 | bool Packages::Find(const char *name) { 52 | if (!name) return false; 53 | return std::find(packages.begin(), packages.end(), name) != packages.end(); 54 | } 55 | 56 | void Packages::Add(const char *name) { 57 | if (!name) return; 58 | packages.emplace_back(name); 59 | 60 | LOGD("package: %s", name); 61 | } 62 | 63 | void Config::Load(std::vector config) { 64 | std::string s_config(config.begin(), config.end()); 65 | LOGD("Config::Load: %s", s_config.c_str()); 66 | std::string app; 67 | std::stringstream content(s_config.c_str()); 68 | while(std::getline(content, app, '\n')) { 69 | if(app.length() == 0) { 70 | continue; 71 | } 72 | LOGD("Config::Load: %s", app.c_str()); 73 | Packages::Add(app.c_str()); 74 | } 75 | LOGD("Config::Load app list loaded."); 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /zygisk/jni/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Config { 7 | 8 | struct Property { 9 | 10 | std::string name; 11 | std::string value; 12 | 13 | Property(const char *name, const char *value) : name(name), value(value) {} 14 | }; 15 | 16 | void Load(std::vector config); 17 | 18 | namespace Properties { 19 | 20 | Property *Find(const char *name); 21 | } 22 | 23 | namespace Packages { 24 | 25 | bool Find(const char *name); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /zygisk/jni/external/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | # libxhook.a 4 | include $(CLEAR_VARS) 5 | LOCAL_MODULE:= libxhook 6 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/libxhook/jni 7 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) 8 | LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden 9 | LOCAL_CONLYFLAGS := -std=c11 10 | LOCAL_SRC_FILES := \ 11 | xhook/libxhook/jni/xh_log.c \ 12 | xhook/libxhook/jni/xh_version.c \ 13 | xhook/libxhook/jni/xh_jni.c \ 14 | xhook/libxhook/jni/xhook.c \ 15 | xhook/libxhook/jni/xh_core.c \ 16 | xhook/libxhook/jni/xh_util.c \ 17 | xhook/libxhook/jni/xh_elf.c 18 | include $(BUILD_STATIC_LIBRARY) 19 | -------------------------------------------------------------------------------- /zygisk/jni/hook.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "logging.h" 7 | #include "android.h" 8 | #include "hook.h" 9 | #include "config.h" 10 | 11 | #define XHOOK_REGISTER(NAME) \ 12 | if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) != 0) { \ 13 | LOGE("failed to register hook " #NAME "."); \ 14 | } 15 | 16 | #define NEW_FUNC_DEF(ret, func, ...) \ 17 | static ret (*old_##func)(__VA_ARGS__); \ 18 | static ret new_##func(__VA_ARGS__) 19 | 20 | NEW_FUNC_DEF(int, __system_property_get, const char *key, char *value) { 21 | int res = old___system_property_get(key, value); 22 | auto prop = Config::Properties::Find(key); 23 | if (prop) { 24 | LOGI("system_property_get: %s=%s -> %s", key, value, prop->value.c_str()); 25 | strcpy(value, prop->value.c_str()); 26 | res = (int) prop->value.length(); 27 | } 28 | return res; 29 | } 30 | 31 | using callback_func = void(void *cookie, const char *name, const char *value, uint32_t serial); 32 | 33 | thread_local callback_func *saved_callback = nullptr; 34 | 35 | static void my_callback(void *cookie, const char *name, const char *value, uint32_t serial) { 36 | if (!saved_callback) return; 37 | auto prop = Config::Properties::Find(name); 38 | if (!prop) { 39 | saved_callback(cookie, name, value, serial); 40 | return; 41 | } 42 | 43 | LOGI("system_property_read_callback: %s=%s -> %s", name, value, prop->value.c_str()); 44 | saved_callback(cookie, name, prop->value.c_str(), serial); 45 | } 46 | 47 | NEW_FUNC_DEF(void, __system_property_read_callback, const prop_info *pi, callback_func *callback, void *cookie) { 48 | saved_callback = callback; 49 | old___system_property_read_callback(pi, my_callback, cookie); 50 | } 51 | 52 | void Hook::install() { 53 | XHOOK_REGISTER(__system_property_get); 54 | 55 | if (android::GetApiLevel() >= 26) { 56 | XHOOK_REGISTER(__system_property_read_callback); 57 | } 58 | 59 | if (xhook_refresh(0) == 0) 60 | xhook_clear(); 61 | else 62 | LOGE("failed to refresh hook"); 63 | } 64 | -------------------------------------------------------------------------------- /zygisk/jni/hook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Hook { 4 | void install(); 5 | } 6 | -------------------------------------------------------------------------------- /zygisk/jni/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOGGING_H 2 | #define _LOGGING_H 3 | 4 | #include 5 | #include "android/log.h" 6 | 7 | #ifndef LOG_TAG 8 | #define LOG_TAG "DeviceFaker" 9 | #endif 10 | 11 | #ifdef DEBUG 12 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 13 | #else 14 | #define LOGD(...) 15 | #endif 16 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 17 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 18 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 19 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 20 | #define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 21 | 22 | #endif // _LOGGING_H 23 | -------------------------------------------------------------------------------- /zygisk/jni/module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "logging.h" 10 | #include "hook.h" 11 | #include "android.h" 12 | #include "config.h" 13 | #include "zygisk.hpp" 14 | 15 | using zygisk::Api; 16 | using zygisk::AppSpecializeArgs; 17 | using zygisk::ServerSpecializeArgs; 18 | 19 | static constexpr auto CONFIG_PATH = "/data/adb/modules/devicefaker/config"; 20 | 21 | class DeviceFakerModule : public zygisk::ModuleBase { 22 | public: 23 | void onLoad(Api *api, JNIEnv *env) override { 24 | this->api = api; 25 | this->env = env; 26 | loadPayload(); 27 | Config::Load(config); 28 | LOGD("Config loaded"); 29 | 30 | } 31 | 32 | void preAppSpecialize(AppSpecializeArgs *args) override { 33 | // Use JNI to fetch our process name 34 | const char *process = env->GetStringUTFChars(args->nice_name, nullptr); 35 | // Use UNI to fetch our user dir 36 | const char *app_data_dir = env->GetStringUTFChars(args->app_data_dir, nullptr); 37 | 38 | if (process == nullptr || app_data_dir == nullptr) { 39 | return; 40 | } 41 | if(*process) { 42 | sscanf(process, "%s", saved_process_name); 43 | } 44 | 45 | if(*app_data_dir) { 46 | // /data/user// 47 | if (sscanf(app_data_dir, "/data/%*[^/]/%d/%s", &saved_uid, saved_package_name) == 2) 48 | goto found; 49 | 50 | // /mnt/expand//user// 51 | if (sscanf(app_data_dir, "/mnt/expand/%*[^/]/%*[^/]/%d/%s", &saved_uid, saved_package_name) == 2) 52 | goto found; 53 | 54 | // /data/data/ 55 | if (sscanf(app_data_dir, "/data/%*[^/]/%s", saved_package_name) == 1) 56 | goto found; 57 | 58 | // nothing found 59 | saved_package_name[0] = '\0'; 60 | 61 | found:; 62 | } 63 | env->ReleaseStringUTFChars(args->nice_name, process); 64 | env->ReleaseStringUTFChars(args->app_data_dir, app_data_dir); 65 | 66 | preSpecialize(saved_package_name); 67 | } 68 | 69 | void preServerSpecialize(ServerSpecializeArgs *args) override { 70 | api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); 71 | } 72 | 73 | void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { 74 | LOGD("uid=%d, package=%s, process=%s", saved_uid, saved_package_name, saved_process_name); 75 | // Inject if module was loaded, otherwise this would've been unloaded by now (for non-GMS) 76 | if (hook) { 77 | LOGI("Install hook... for %d:%s", saved_uid / 100000, saved_package_name); 78 | injectBuild(env); 79 | Hook::install(); 80 | LOGI("Hook installed"); 81 | } 82 | 83 | } 84 | 85 | private: 86 | Api *api; 87 | JNIEnv *env; 88 | 89 | std::vector config; 90 | bool hook = false; 91 | char saved_process_name[256] = {0}; 92 | char saved_package_name[256] = {0}; 93 | int saved_uid; 94 | 95 | static int receiveFile(int remote_fd, std::vector& buf) { 96 | off_t size; 97 | int ret = read(remote_fd, &size, sizeof(size)); 98 | if (ret < 0) { 99 | LOGE("Failed to read size"); 100 | return -1; 101 | } 102 | 103 | buf.resize(size); 104 | 105 | int bytesReceived = 0; 106 | while (bytesReceived < size) { 107 | ret = read(remote_fd, buf.data() + bytesReceived, size - bytesReceived); 108 | if (ret < 0) { 109 | LOGE("Failed to read data"); 110 | return -1; 111 | } 112 | 113 | bytesReceived += ret; 114 | } 115 | 116 | return bytesReceived; 117 | } 118 | 119 | void loadPayload() { 120 | auto fd = api->connectCompanion(); 121 | 122 | auto size = receiveFile(fd, config); 123 | LOGD("Loaded module payload: %d bytes", size); 124 | 125 | close(fd); 126 | } 127 | 128 | void preSpecialize(const char *package) { 129 | if(Config::Packages::Find(package) == false) { 130 | LOGD("devicefaker: preSpecialize: package=[%s]", package); 131 | 132 | api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY); 133 | hook = false; 134 | return; 135 | } 136 | hook = true; 137 | 138 | // Force DenyList unmounting for all GMS processes 139 | api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); 140 | 141 | 142 | } 143 | 144 | void injectBuild(JNIEnv *env) { 145 | if (env == nullptr) { 146 | LOGW("failed to inject android.os.Build for %s due to env is null", saved_package_name); 147 | return; 148 | } 149 | LOGI("inject android.os.Build for %s ", saved_package_name); 150 | 151 | jclass build_class = env->FindClass("android/os/Build"); 152 | if (build_class == nullptr) { 153 | LOGW("failed to inject android.os.Build for %s due to build is null", saved_package_name); 154 | return; 155 | } 156 | 157 | jstring new_brand = env->NewStringUTF("samsung"); 158 | jstring new_product = env->NewStringUTF("gts7l"); 159 | jstring new_model = env->NewStringUTF("SM-T875"); 160 | 161 | jfieldID brand_id = env->GetStaticFieldID(build_class, "BRAND", "Ljava/lang/String;"); 162 | if (brand_id != nullptr) { 163 | env->SetStaticObjectField(build_class, brand_id, new_brand); 164 | } 165 | 166 | jfieldID manufacturer_id = env->GetStaticFieldID(build_class, "MANUFACTURER", "Ljava/lang/String;"); 167 | if (manufacturer_id != nullptr) { 168 | env->SetStaticObjectField(build_class, manufacturer_id, new_brand); 169 | } 170 | 171 | jfieldID product_id = env->GetStaticFieldID(build_class, "PRODUCT", "Ljava/lang/String;"); 172 | if (product_id != nullptr) { 173 | env->SetStaticObjectField(build_class, product_id, new_product); 174 | } 175 | 176 | jfieldID device_id = env->GetStaticFieldID(build_class, "DEVICE", "Ljava/lang/String;"); 177 | if (device_id != nullptr) { 178 | env->SetStaticObjectField(build_class, device_id, new_product); 179 | } 180 | 181 | jfieldID model_id = env->GetStaticFieldID(build_class, "MODEL", "Ljava/lang/String;"); 182 | if (model_id != nullptr) { 183 | env->SetStaticObjectField(build_class, model_id, new_model); 184 | } 185 | 186 | 187 | if(env->ExceptionCheck()) 188 | { 189 | env->ExceptionClear(); 190 | } 191 | 192 | env->DeleteLocalRef(new_brand); 193 | env->DeleteLocalRef(new_product); 194 | env->DeleteLocalRef(new_model); 195 | } 196 | 197 | 198 | 199 | }; 200 | 201 | static off_t sendFile(int remote_fd, const std::string& path) { 202 | auto in_fd = open(path.c_str(), O_RDONLY); 203 | if (in_fd < 0) { 204 | LOGE("Failed to open file %s: %d (%s)", path.c_str(), errno, strerror(errno)); 205 | return -1; 206 | } 207 | 208 | auto size = lseek(in_fd, 0, SEEK_END); 209 | if (size < 0) { 210 | LOGI("Failed to get file size"); 211 | close(in_fd); 212 | return -1; 213 | } 214 | lseek(in_fd, 0, SEEK_SET); 215 | 216 | // Send size first for buffer allocation 217 | int ret = write(remote_fd, &size, sizeof(size)); 218 | if (ret < 0) { 219 | LOGI("Failed to send size"); 220 | close(in_fd); 221 | return -1; 222 | } 223 | 224 | ret = sendfile(remote_fd, in_fd, nullptr, size); 225 | if (ret < 0) { 226 | LOGI("Failed to send data"); 227 | close(in_fd); 228 | return -1; 229 | } 230 | 231 | close(in_fd); 232 | return size; 233 | } 234 | 235 | static void companionHandler(int remote_fd) { 236 | // Serve module dex 237 | auto size = sendFile(remote_fd, CONFIG_PATH); 238 | LOGD("Sent module payload: %ld bytes", size); 239 | } 240 | 241 | REGISTER_ZYGISK_COMPANION(companionHandler) 242 | REGISTER_ZYGISK_MODULE(DeviceFakerModule) 243 | -------------------------------------------------------------------------------- /zygisk/jni/zygisk.hpp: -------------------------------------------------------------------------------- 1 | // This is the public API for Zygisk modules. 2 | // DO NOT MODIFY ANY CODE IN THIS HEADER. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define ZYGISK_API_VERSION 3 9 | 10 | /* 11 | 12 | Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. 13 | Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. 14 | 15 | Please note that modules will only be loaded after zygote has forked the child process. 16 | THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM SERVER PROCESS, NOT THE ZYGOTE DAEMON! 17 | 18 | Example code: 19 | 20 | static jint (*orig_logger_entry_max)(JNIEnv *env); 21 | static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } 22 | 23 | static void example_handler(int socket) { ... } 24 | 25 | class ExampleModule : public zygisk::ModuleBase { 26 | public: 27 | void onLoad(zygisk::Api *api, JNIEnv *env) override { 28 | this->api = api; 29 | this->env = env; 30 | } 31 | void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { 32 | JNINativeMethod methods[] = { 33 | { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, 34 | }; 35 | api->hookJniNativeMethods(env, "android/util/Log", methods, 1); 36 | *(void **) &orig_logger_entry_max = methods[0].fnPtr; 37 | } 38 | private: 39 | zygisk::Api *api; 40 | JNIEnv *env; 41 | }; 42 | 43 | REGISTER_ZYGISK_MODULE(ExampleModule) 44 | 45 | REGISTER_ZYGISK_COMPANION(example_handler) 46 | 47 | */ 48 | 49 | namespace zygisk { 50 | 51 | struct Api; 52 | struct AppSpecializeArgs; 53 | struct ServerSpecializeArgs; 54 | 55 | class ModuleBase { 56 | public: 57 | 58 | // This function is called when the module is loaded into the target process. 59 | // A Zygisk API handle will be sent as an argument; call utility functions or interface 60 | // with Zygisk through this handle. 61 | virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {} 62 | 63 | // This function is called before the app process is specialized. 64 | // At this point, the process just got forked from zygote, but no app specific specialization 65 | // is applied. This means that the process does not have any sandbox restrictions and 66 | // still runs with the same privilege of zygote. 67 | // 68 | // All the arguments that will be sent and used for app specialization is passed as a single 69 | // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app 70 | // process will be specialized. 71 | // 72 | // If you need to run some operations as superuser, you can call Api::connectCompanion() to 73 | // get a socket to do IPC calls with a root companion process. 74 | // See Api::connectCompanion() for more info. 75 | virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {} 76 | 77 | // This function is called after the app process is specialized. 78 | // At this point, the process has all sandbox restrictions enabled for this application. 79 | // This means that this function runs as the same privilege of the app's own code. 80 | virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} 81 | 82 | // This function is called before the system server process is specialized. 83 | // See preAppSpecialize(args) for more info. 84 | virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {} 85 | 86 | // This function is called after the system server process is specialized. 87 | // At this point, the process runs with the privilege of system_server. 88 | virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {} 89 | }; 90 | 91 | struct AppSpecializeArgs { 92 | // Required arguments. These arguments are guaranteed to exist on all Android versions. 93 | jint &uid; 94 | jint &gid; 95 | jintArray &gids; 96 | jint &runtime_flags; 97 | jobjectArray &rlimits; 98 | jint &mount_external; 99 | jstring &se_info; 100 | jstring &nice_name; 101 | jstring &instruction_set; 102 | jstring &app_data_dir; 103 | 104 | // Optional arguments. Please check whether the pointer is null before de-referencing 105 | jintArray *const fds_to_ignore; 106 | jboolean *const is_child_zygote; 107 | jboolean *const is_top_app; 108 | jobjectArray *const pkg_data_info_list; 109 | jobjectArray *const whitelisted_data_info_list; 110 | jboolean *const mount_data_dirs; 111 | jboolean *const mount_storage_dirs; 112 | 113 | AppSpecializeArgs() = delete; 114 | }; 115 | 116 | struct ServerSpecializeArgs { 117 | jint &uid; 118 | jint &gid; 119 | jintArray &gids; 120 | jint &runtime_flags; 121 | jlong &permitted_capabilities; 122 | jlong &effective_capabilities; 123 | 124 | ServerSpecializeArgs() = delete; 125 | }; 126 | 127 | namespace internal { 128 | struct api_table; 129 | template void entry_impl(api_table *, JNIEnv *); 130 | } 131 | 132 | // These values are used in Api::setOption(Option) 133 | enum Option : int { 134 | // Force Magisk's denylist unmount routines to run on this process. 135 | // 136 | // Setting this option only makes sense in preAppSpecialize. 137 | // The actual unmounting happens during app process specialization. 138 | // 139 | // Set this option to force all Magisk and modules' files to be unmounted from the 140 | // mount namespace of the process, regardless of the denylist enforcement status. 141 | FORCE_DENYLIST_UNMOUNT = 0, 142 | 143 | // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. 144 | // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory. 145 | // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. 146 | DLCLOSE_MODULE_LIBRARY = 1, 147 | }; 148 | 149 | // Bit masks of the return value of Api::getFlags() 150 | enum StateFlag : uint32_t { 151 | // The user has granted root access to the current process 152 | PROCESS_GRANTED_ROOT = (1u << 0), 153 | 154 | // The current process was added on the denylist 155 | PROCESS_ON_DENYLIST = (1u << 1), 156 | }; 157 | 158 | // All API functions will stop working after post[XXX]Specialize as Zygisk will be unloaded 159 | // from the specialized process afterwards. 160 | struct Api { 161 | 162 | // Connect to a root companion process and get a Unix domain socket for IPC. 163 | // 164 | // This API only works in the pre[XXX]Specialize functions due to SELinux restrictions. 165 | // 166 | // The pre[XXX]Specialize functions run with the same privilege of zygote. 167 | // If you would like to do some operations with superuser permissions, register a handler 168 | // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). 169 | // Another good use case for a companion process is that if you want to share some resources 170 | // across multiple processes, hold the resources in the companion process and pass it over. 171 | // 172 | // The root companion process is ABI aware; that is, when calling this function from a 32-bit 173 | // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. 174 | // 175 | // Returns a file descriptor to a socket that is connected to the socket passed to your 176 | // module's companion request handler. Returns -1 if the connection attempt failed. 177 | int connectCompanion(); 178 | 179 | // Get the file descriptor of the root folder of the current module. 180 | // 181 | // This API only works in the pre[XXX]Specialize functions. 182 | // Accessing the directory returned is only possible in the pre[XXX]Specialize functions 183 | // or in the root companion process (assuming that you sent the fd over the socket). 184 | // Both restrictions are due to SELinux and UID. 185 | // 186 | // Returns -1 if errors occurred. 187 | int getModuleDir(); 188 | 189 | // Set various options for your module. 190 | // Please note that this function accepts one single option at a time. 191 | // Check zygisk::Option for the full list of options available. 192 | void setOption(Option opt); 193 | 194 | // Get information about the current process. 195 | // Returns bitwise-or'd zygisk::StateFlag values. 196 | uint32_t getFlags(); 197 | 198 | // Hook JNI native methods for a class 199 | // 200 | // Lookup all registered JNI native methods and replace it with your own functions. 201 | // The original function pointer will be saved in each JNINativeMethod's fnPtr. 202 | // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr 203 | // will be set to nullptr. 204 | void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); 205 | 206 | // For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`. 207 | // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. 208 | void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc); 209 | 210 | // For ELFs loaded in memory matching `regex`, exclude hooks registered for `symbol`. 211 | // If `symbol` is nullptr, then all symbols will be excluded. 212 | void pltHookExclude(const char *regex, const char *symbol); 213 | 214 | // Commit all the hooks that was previously registered. 215 | // Returns false if an error occurred. 216 | bool pltHookCommit(); 217 | 218 | private: 219 | internal::api_table *impl; 220 | template friend void internal::entry_impl(internal::api_table *, JNIEnv *); 221 | }; 222 | 223 | // Register a class as a Zygisk module 224 | 225 | #define REGISTER_ZYGISK_MODULE(clazz) \ 226 | void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ 227 | zygisk::internal::entry_impl(table, env); \ 228 | } 229 | 230 | // Register a root companion request handler function for your module 231 | // 232 | // The function runs in a superuser daemon process and handles a root companion request from 233 | // your module running in a target process. The function has to accept an integer value, 234 | // which is a socket that is connected to the target process. 235 | // See Api::connectCompanion() for more info. 236 | // 237 | // NOTE: the function can run concurrently on multiple threads. 238 | // Be aware of race conditions if you have a globally shared resource. 239 | 240 | #define REGISTER_ZYGISK_COMPANION(func) \ 241 | void zygisk_companion_entry(int client) { func(client); } 242 | 243 | /************************************************************************************ 244 | * All the code after this point is internal code used to interface with Zygisk 245 | * and guarantee ABI stability. You do not have to understand what it is doing. 246 | ************************************************************************************/ 247 | 248 | namespace internal { 249 | 250 | struct module_abi { 251 | long api_version; 252 | ModuleBase *_this; 253 | 254 | void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); 255 | void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); 256 | void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); 257 | void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); 258 | 259 | module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), _this(module) { 260 | preAppSpecialize = [](auto self, auto args) { self->preAppSpecialize(args); }; 261 | postAppSpecialize = [](auto self, auto args) { self->postAppSpecialize(args); }; 262 | preServerSpecialize = [](auto self, auto args) { self->preServerSpecialize(args); }; 263 | postServerSpecialize = [](auto self, auto args) { self->postServerSpecialize(args); }; 264 | } 265 | }; 266 | 267 | struct api_table { 268 | // These first 2 entries are permanent, shall never change 269 | void *_this; 270 | bool (*registerModule)(api_table *, module_abi *); 271 | 272 | // Utility functions 273 | void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); 274 | void (*pltHookRegister)(const char *, const char *, void *, void **); 275 | void (*pltHookExclude)(const char *, const char *); 276 | bool (*pltHookCommit)(); 277 | 278 | // Zygisk functions 279 | int (*connectCompanion)(void * /* _this */); 280 | void (*setOption)(void * /* _this */, Option); 281 | int (*getModuleDir)(void * /* _this */); 282 | uint32_t (*getFlags)(void * /* _this */); 283 | }; 284 | 285 | template 286 | void entry_impl(api_table *table, JNIEnv *env) { 287 | ModuleBase *module = new T(); 288 | if (!table->registerModule(table, new module_abi(module))) 289 | return; 290 | auto api = new Api(); 291 | api->impl = table; 292 | module->onLoad(api, env); 293 | } 294 | 295 | } // namespace internal 296 | 297 | inline int Api::connectCompanion() { 298 | return impl->connectCompanion ? impl->connectCompanion(impl->_this) : -1; 299 | } 300 | inline int Api::getModuleDir() { 301 | return impl->getModuleDir ? impl->getModuleDir(impl->_this) : -1; 302 | } 303 | inline void Api::setOption(Option opt) { 304 | if (impl->setOption) impl->setOption(impl->_this, opt); 305 | } 306 | inline uint32_t Api::getFlags() { 307 | return impl->getFlags ? impl->getFlags(impl->_this) : 0; 308 | } 309 | inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { 310 | if (impl->hookJniNativeMethods) impl->hookJniNativeMethods(env, className, methods, numMethods); 311 | } 312 | inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { 313 | if (impl->pltHookRegister) impl->pltHookRegister(regex, symbol, newFunc, oldFunc); 314 | } 315 | inline void Api::pltHookExclude(const char *regex, const char *symbol) { 316 | if (impl->pltHookExclude) impl->pltHookExclude(regex, symbol); 317 | } 318 | inline bool Api::pltHookCommit() { 319 | return impl->pltHookCommit != nullptr && impl->pltHookCommit(); 320 | } 321 | 322 | } // namespace zygisk 323 | 324 | [[gnu::visibility("default")]] [[gnu::used]] 325 | extern "C" void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); 326 | 327 | [[gnu::visibility("default")]] [[gnu::used]] 328 | extern "C" void zygisk_companion_entry(int); 329 | -------------------------------------------------------------------------------- /zygisk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------