├── .gitignore ├── LICENSE ├── README.md ├── ZIP └── suhide │ ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script │ └── common │ ├── add │ ├── list │ ├── rm │ ├── switch_packages │ └── zz99suhide ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── suhide ├── build.gradle ├── native ├── Android.mk ├── Application.mk ├── config.c ├── config.h ├── getevent.c ├── getevent.h ├── ndklog.h ├── setpropex │ ├── Android.mk │ ├── LICENSE │ ├── _system_properties.h │ ├── inc │ │ └── cutils │ │ │ └── properties.h │ ├── setpropex.c │ ├── system_properties.c │ └── system_properties_compat.c ├── suhide.c ├── suhide_launcher.c ├── trace.c ├── trace.h ├── util.c └── util.h ├── proguard-project.txt ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── eu │ └── chainfire │ └── suhide │ ├── AppAdapter.java │ ├── AppFragment.java │ ├── MainActivity.java │ ├── MyApplication.java │ └── TextFragment.java └── res ├── drawable ├── ic_error.xml ├── ic_hidden.xml ├── ic_kill.xml ├── ic_no_root.xml └── ic_root.xml ├── layout ├── activity_main.xml ├── fragment_app.xml ├── fragment_app_list.xml └── fragment_main.xml ├── mipmap-hdpi └── ic_launcher.png ├── mipmap-mdpi └── ic_launcher.png ├── mipmap-xhdpi └── ic_launcher.png ├── mipmap-xxhdpi └── ic_launcher.png ├── mipmap-xxxhdpi └── ic_launcher.png ├── values-v21 └── styles.xml ├── values-w820dp └── dimens.xml └── values ├── colors.xml ├── dimens.xml ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | *.iml 4 | local.properties 5 | */bin 6 | */gen 7 | */build 8 | 9 | suhide/proguard 10 | ZIP/suhide/common/*.apk 11 | ZIP/suhude/arm 12 | ZIP/suhude/armv7 13 | ZIP/suhude/arm64 14 | ZIP/suhude/x86 15 | ZIP/suhude/x64 16 | ZIP/suhude/mips 17 | ZIP/suhude/mips64 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | There are the sources for SuperSU's **suhide-lite** mod. 2 | 3 | At time of writing, these correspond to **v1.09**. 4 | 5 | Sources for the (uninteresting) Java parts can be found in **suhide/src**. 6 | 7 | Sources for the (interesting) C parts can be found in **suhide/native**. 8 | 9 | The (release) build process will output files to **ZIP/suhide**. 10 | 11 | The folder structure is like this because it is copied out of the larger build tree used for SuperSU builds. The script that builds the actual flashable ZIP from the **ZIP/suhide** folder is missing as it depends on internal tools. 12 | 13 | The gradle build script depends on an (included) older Gradle version and handles building with the NDK manually. 14 | 15 | Your *local.properties* file needs correct paths set for both *sdk.dir* as well as *ndk.dir* variables. 16 | 17 | Builds have been tested with NDK **r10e** specifically, on a Windows machine. 18 | 19 | 20 | Published with permission from CCMT 21 | -------------------------------------------------------------------------------- /ZIP/suhide/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | OUTFD=$2 4 | ZIP=$3 5 | 6 | ui_print() { 7 | echo -n -e "ui_print $1\n" > /proc/self/fd/$OUTFD 8 | echo -n -e "ui_print\n" > /proc/self/fd/$OUTFD 9 | } 10 | 11 | ui_print " " 12 | ui_print "*************************" 13 | ui_print "Installing suhide-lite..." 14 | ui_print "*************************" 15 | ui_print " " 16 | 17 | mount -o ro /system 18 | mount /system 19 | mount -o rw,remount / 20 | 21 | cd /tmp 22 | mkdir suhide 23 | cd suhide 24 | 25 | unzip -o "$ZIP" 26 | 27 | # some checks 28 | 29 | ui_print "Compatibility checks..." 30 | ui_print " " 31 | 32 | if [ -f "/system/xbin/su" ]; then 33 | ui_print "Fatal error: /system/xbin/su found" 34 | ui_print "Do you have SuperSU v2.82 SR2 or newer installed in systemless mode ?" 35 | exit 1 36 | fi 37 | 38 | if [ -f "/system/bin/su" ]; then 39 | ui_print "Fatal error: /system/bin/su found" 40 | ui_print "Do you have SuperSU v2.82 SR2 or newer installed in systemless mode ?" 41 | exit 1 42 | fi 43 | 44 | if [ -f "/system/bin/app_process32_xposed" ]; then 45 | ui_print "Fatal error: /system/bin/app_process32_xposed found" 46 | ui_print "Xposed is not currently supported" 47 | exit 1 48 | fi 49 | 50 | if [ -f "/system/bin/app_process64_xposed" ]; then 51 | ui_print "Fatal error: /system/bin/app_process64_xposed found" 52 | ui_print "Xposed is not currently supported" 53 | exit 1 54 | fi 55 | 56 | if [ ! -d "/data/supersu_install" ] && [ ! -d "/cache/supersu_install" ]; then 57 | if [ -f "/data/su.img" ]; then 58 | ui_print "Fatal error: SuperSU image found at /data/su.img" 59 | ui_print "SuperSU must be installed in BINDSBIN mode" 60 | exit 1 61 | fi 62 | 63 | if [ -f "/cache/su.img" ]; then 64 | ui_print "Fatal error: SuperSU image found at /cache/su.img" 65 | ui_print "SuperSU must be installed in BINDSBIN mode" 66 | exit 1 67 | fi 68 | fi 69 | 70 | SUPATH= 71 | if [ -d "/data/adb/su/bin" ]; then 72 | SUPATH=/data/adb/su 73 | elif [ -d "/data/supersu_install/bin" ]; then 74 | SUPATH=/data/supersu_install 75 | elif [ -d "/cache/supersu_install/bin" ]; then 76 | SUPATH=/cache/supersu_install 77 | fi 78 | 79 | if [ -z "$SUPATH" ]; then 80 | ui_print "Fatal error: SuperSU binaries not found" 81 | ui_print "SuperSU must be installed in BINDSBIN mode" 82 | exit 1 83 | fi 84 | 85 | # detect architecture 86 | 87 | ui_print "Detecting architecture..." 88 | 89 | ABI=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi=" | dd bs=1 skip=19 count=3) 90 | ABILONG=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi=" | dd bs=1 skip=19) 91 | ABI2=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi2=" | dd bs=1 skip=20 count=3) 92 | ARCH=arm 93 | 94 | if [ "$ABI" = "x86" ]; then ARCH=x86; fi; 95 | if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; 96 | 97 | if [ "$ABILONG" = "armeabi-v7a" ]; then ARCH=armv7; fi; 98 | if [ "$ABI" = "mip" ]; then ARCH=mips; fi; 99 | if [ "$ABILONG" = "mips" ]; then ARCH=mips; fi; 100 | 101 | if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; fi; 102 | if [ "$ABILONG" = "mips64" ]; then ARCH=mips64; fi; 103 | if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; fi; 104 | 105 | ui_print "- $ARCH" 106 | ui_print " " 107 | 108 | # place files 109 | 110 | ui_print "Copying files..." 111 | 112 | mkdir $SUPATH/suhide 113 | chown 0.0 $SUPATH/suhide 114 | chmod 0755 $SUPATH/suhide 115 | chcon u:object_r:system_file:s0 $SUPATH/suhide 116 | 117 | for FILE in setpropex suhide suhide32 suhide64; do 118 | cp /tmp/suhide/$ARCH/$FILE $SUPATH/suhide/$FILE 119 | chown 0.0 $SUPATH/suhide/$FILE 120 | chmod 0755 $SUPATH/suhide/$FILE 121 | chcon u:object_r:system_file:s0 $SUPATH/suhide/$FILE 122 | done 123 | 124 | for FILE in add list rm switch_packages; do 125 | cp /tmp/suhide/common/$FILE $SUPATH/suhide/$FILE 126 | chown 0.0 $SUPATH/suhide/$FILE 127 | chmod 0755 $SUPATH/suhide/$FILE 128 | chcon u:object_r:system_file:s0 $SUPATH/suhide/$FILE 129 | done 130 | 131 | for FILE in suhide.apk; do 132 | cp /tmp/suhide/common/$FILE $SUPATH/suhide/$FILE 133 | chown 0.0 $SUPATH/suhide/$FILE 134 | chmod 0644 $SUPATH/suhide/$FILE 135 | chcon u:object_r:system_file:s0 $SUPATH/suhide/$FILE 136 | done 137 | 138 | rm $SUPATH/su.d/*suhide* 139 | cp /tmp/suhide/common/zz99suhide $SUPATH/su.d/zz99suhide 140 | chown 0.0 $SUPATH/su.d/zz99suhide 141 | chmod 0700 $SUPATH/su.d/zz99suhide 142 | chcon u:object_r:system_file:s0 $SUPATH/su.d/zz99suhide 143 | 144 | ui_print "- Complete" 145 | ui_print " " 146 | 147 | ui_print "Creating defaults..." 148 | if [ ! -f "$SUPATH/suhide/suhide.uid" ]; then 149 | echo com.google.android.gms.unstable>$SUPATH/suhide/suhide.uid 150 | fi 151 | chmod 0600 $SUPATH/suhide/suhide.uid 152 | chcon u:object_r:system_file:s0 $SUPATH/suhide/suhide.uid 153 | if [ ! -f "$SUPATH/suhide/suhide.pkg" ]; then 154 | echo eu.chainfire.supersu>$SUPATH/suhide/suhide.pkg 155 | echo eu.chainfire.suhide>>$SUPATH/suhide/suhide.pkg 156 | fi 157 | chmod 0600 $SUPATH/suhide/suhide.pkg 158 | chcon u:object_r:system_file:s0 $SUPATH/suhide/suhide.pkg 159 | ui_print "- Complete" 160 | ui_print " " 161 | 162 | # done 163 | 164 | cd /tmp 165 | rm -rf /tmp/suhide 166 | cd / 167 | 168 | ui_print "**********************" 169 | ui_print "Installation complete!" 170 | ui_print "**********************" 171 | ui_print " " 172 | 173 | exit 0 174 | -------------------------------------------------------------------------------- /ZIP/suhide/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #dummy file 2 | -------------------------------------------------------------------------------- /ZIP/suhide/common/add: -------------------------------------------------------------------------------- 1 | #!/sbin/sush 2 | UIDFILE=/sbin/supersu/suhide/suhide.uid 3 | UID=$1 4 | 5 | if [ ! -z "$UID" ]; then 6 | if (! cat $UIDFILE 2>/dev/null | grep "^$UID$" >/dev/null); then 7 | echo "$UID" >> $UIDFILE 8 | fi 9 | fi 10 | -------------------------------------------------------------------------------- /ZIP/suhide/common/list: -------------------------------------------------------------------------------- 1 | #!/sbin/sush 2 | UIDFILE=/sbin/supersu/suhide/suhide.uid 3 | cat $UIDFILE 4 | -------------------------------------------------------------------------------- /ZIP/suhide/common/rm: -------------------------------------------------------------------------------- 1 | #!/sbin/sush 2 | UIDFILE=/sbin/supersu/suhide/suhide.uid 3 | UID=$1 4 | 5 | if [ ! -z "$UID" ]; then 6 | cat $UIDFILE | grep -v "^$UID$" > $UIDFILE.tmp 7 | if [ -f "$UIDFILE.tmp" ]; then 8 | rm $UIDFILE 9 | mv $UIDFILE.tmp $UIDFILE 10 | fi 11 | fi 12 | -------------------------------------------------------------------------------- /ZIP/suhide/common/switch_packages: -------------------------------------------------------------------------------- 1 | #!/sbin/sush 2 | PKGFILE=/sbin/supersu/suhide/suhide.pkg 3 | 4 | HIDDEN=false 5 | for i in `cat $PKGFILE 2>/dev/null`; do 6 | if (`cat /data/system/users/0/package-restrictions.xml 2>/dev/null | grep $i | grep hidden >/dev/null`); then 7 | HIDDEN=true 8 | fi 9 | done 10 | for i in `cat $PKGFILE 2>/dev/null`; do 11 | if (! $HIDDEN); then 12 | pm hide $i 13 | else 14 | pm unhide $i 15 | fi 16 | done 17 | -------------------------------------------------------------------------------- /ZIP/suhide/common/zz99suhide: -------------------------------------------------------------------------------- 1 | #!/sbin/sush 2 | 3 | setprop() { 4 | /sbin/supersu/suhide/setpropex $1 $2 /sbin/supersu/suhide/setpropex.lock 5 | } 6 | 7 | { 8 | setprop ro.boot.verifiedbootstate green 9 | setprop ro.boot.veritymode enforcing 10 | setprop ro.boot.flash.locked 1 11 | setprop ro.oem_unlock_supported 0 12 | setprop sys.oem_unlock_allowed 0 13 | setprop ro.debuggable 0 14 | setprop ro.secure 1 15 | setprop ro.adb.secure 1 16 | } & 17 | 18 | for PROP in ro.bootimage.build.fingerprint ro.build.description ro.build.fingerprint ro.build.tags ro.vendor.build.fingerprint; do 19 | { 20 | local CURRENT=$(getprop $PROP) 21 | if [ ! -z "$CURRENT" ]; then 22 | CURRENT=$(echo $CURRENT | sed 's/:userdebug\//:user\//g' | sed 's/:eng\//:user\//g' | sed 's/user-keys/release-keys/g' | sed 's/test-keys/release-keys/g') 23 | setprop $PROP "$CURRENT" 24 | fi 25 | } & 26 | done 27 | 28 | { 29 | mount -o rw,remount rootfs / 30 | sed -i '/supersu_prop/d' /property_contexts 31 | restorecon /property_contexts 32 | sed -i '/supersu_prop/d' /plat_property_contexts 33 | restorecon /plat_property_contexts 34 | rm /init.supersu.rc 35 | rm -rf /boot 36 | mount -o ro,remount rootfs / 37 | } & 38 | 39 | wait 40 | 41 | rm /sbin/supersu/suhide/setpropex.lock 42 | 43 | if [ -f "/sbin/supersu/suhide/suhide.apk" ]; then 44 | { 45 | while (true); do 46 | pm uninstall eu.chainfire.suhide 47 | if (`pm install -r /sbin/supersu/suhide/suhide.apk >>/sbin/.suhide_install 2>&1`); then 48 | break; 49 | fi 50 | sleep 1 51 | done 52 | rm -rf /sbin/supersu/suhide/suhide.apk 53 | } & 54 | fi 55 | 56 | /sbin/supersu/suhide/suhide 57 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.3.+' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | jcenter() 14 | mavenLocal() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 26 16:07:58 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':suhide' 2 | -------------------------------------------------------------------------------- /suhide/build.gradle: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.tasks.NdkCompile 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | 4 | apply plugin: 'com.android.application' 5 | 6 | dependencies { 7 | compile fileTree(dir: 'libs', include: ['*.jar']) 8 | compile 'com.android.support:appcompat-v7:25.1.1' 9 | compile 'com.android.support:design:25.1.1' 10 | compile 'com.android.support:recyclerview-v7:25.1.1' 11 | compile('eu.chainfire:libsuperuser:1.0.0.+') { changing = true } 12 | } 13 | 14 | def copyFile(String source, String target) { 15 | delete file(target) 16 | copy { 17 | from file(source).parentFile.absolutePath 18 | into file(target).parentFile.absolutePath 19 | include file(source).name 20 | rename '(.*)', file(target).name 21 | } 22 | } 23 | 24 | def copyFiles(String sourceBase, String source32, String source64, String target) { 25 | if (source64 == null) { 26 | copyFile(sourceBase + '/' + source32 + '/suhide', target + '/suhide'); 27 | copyFile(sourceBase + '/' + source32 + '/suhide32', target + '/suhide32'); 28 | copyFile(sourceBase + '/' + source32 + '/setpropex', target + '/setpropex'); 29 | } else { 30 | copyFile(sourceBase + '/' + source64 + '/suhide', target + '/suhide'); 31 | copyFile(sourceBase + '/' + source32 + '/suhide32', target + '/suhide32'); 32 | copyFile(sourceBase + '/' + source64 + '/suhide64', target + '/suhide64'); 33 | copyFile(sourceBase + '/' + source64 + '/setpropex', target + '/setpropex'); 34 | } 35 | } 36 | 37 | def ndkClean(String path) { 38 | delete file(path.concat('/obj')); 39 | delete file(path.concat('/libs')); 40 | } 41 | 42 | def ndkBuildInt(String path, String abi, String platform, boolean debug, boolean pie, String extra1 = '', String extra2 = '') { 43 | Properties properties = new Properties() 44 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 45 | def ndkDir = properties.getProperty('ndk.dir') 46 | def ndkBuildCommand = ndkDir + ((Os.isFamily(Os.FAMILY_WINDOWS)) ? '\\ndk-build.cmd' : '/ndk-build') 47 | def arguments = [ 48 | '-C', 49 | file(path).absolutePath, 50 | '-j6', 51 | '-B', 52 | 'NDK_TOOLCHAIN_VERSION=4.9', 53 | 'APP_ABI=' + abi, 54 | 'APP_PLATFORM=' + platform, 55 | 'APP_PIE=' + (pie ? 'true' : 'false'), 56 | 'V=' + (debug ? '1' : '0'), 57 | 'NDK_DEBUG=' + (debug ? '1' : '0'), 58 | 'NDK_LOG=' + (debug ? '1' : '0'), 59 | 'NDK_PROJECT_PATH=' + file(path).absolutePath, 60 | 'APP_PROJECT_PATH=' + file(path).absolutePath, 61 | 'APP_BUILD_SCRIPT=Android.mk', 62 | extra1, 63 | extra2 64 | ]; 65 | println ndkBuildCommand + ' ' + arguments.join(' '); 66 | exec { 67 | executable ndkBuildCommand 68 | args arguments 69 | } 70 | } 71 | 72 | def ndkBuild(boolean debug) { 73 | ndkClean('native'); 74 | if (debug) { 75 | ndkBuildInt('native', 'armeabi-v7a,arm64-v8a', 'android-21', true, true); 76 | copyFiles('native/libs', 'armeabi-v7a', null, '../ZIP/suhide/armv7'); 77 | copyFiles('native/libs', 'armeabi-v7a', 'arm64-v8a', '../ZIP/suhide/arm64'); 78 | } else { 79 | ndkBuildInt('native', 'all', 'android-21', false, false); 80 | 81 | copyFiles('native/libs', 'armeabi', null, '../ZIP/suhide/arm'); 82 | copyFiles('native/libs', 'armeabi-v7a', null, '../ZIP/suhide/armv7'); 83 | copyFiles('native/libs', 'armeabi-v7a', 'arm64-v8a', '../ZIP/suhide/arm64'); 84 | 85 | copyFiles('native/libs', 'mips', null, '../ZIP/suhide/mips'); 86 | copyFiles('native/libs', 'mips', 'mips64', '../ZIP/suhide/mips64'); 87 | 88 | copyFiles('native/libs', 'x86', null, '../ZIP/suhide/x86'); 89 | copyFiles('native/libs', 'x86', 'x86_64', '../ZIP/suhide/x64'); 90 | } 91 | ndkClean('native'); 92 | } 93 | 94 | def zipCreate(String type, String outputFile) { 95 | def command = project.rootProject.file('ZIP/make.bat').getAbsolutePath() 96 | def arguments = [ type, outputFile ] 97 | println command + ' ' + arguments.join(' '); 98 | exec { 99 | workingDir project.rootProject.file('ZIP') 100 | executable command 101 | args arguments 102 | } 103 | } 104 | 105 | android { 106 | compileSdkVersion 25 107 | buildToolsVersion "25.0.0" 108 | 109 | defaultConfig { 110 | applicationId "eu.chainfire.suhide" 111 | minSdkVersion 23 112 | targetSdkVersion 25 113 | } 114 | 115 | buildTypes { 116 | release { 117 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' 118 | minifyEnabled true 119 | } 120 | } 121 | 122 | applicationVariants.all { variant -> 123 | variant.outputs.each { output -> 124 | if (variant.buildType.name == 'release') { 125 | def baseName = "suhide" 126 | 127 | output.outputFile = new File(output.outputFile.parent, baseName + "-v" + (new com.android.builder.core.DefaultManifestParser()).getVersionName(android.sourceSets.main.manifest.srcFile) + "-" + (new Date()).format('yyyyMMddHHmmss') + ".apk") 128 | 129 | variant.assemble.doLast { 130 | copy { 131 | from 'build/outputs/mapping/release' 132 | into 'proguard' 133 | include '**/mapping.txt' 134 | } 135 | copyFile('proguard/mapping.txt', output.outputFile.getAbsolutePath().replace('.apk', '-mapping.txt')); 136 | 137 | copyFile(output.outputFile.getAbsolutePath(), project.rootProject.file('ZIP/suhide/common/suhide.apk').getAbsolutePath()); 138 | zipCreate('suhide', output.outputFile.getAbsolutePath().replace('.apk', '.zip')); 139 | } 140 | } 141 | } 142 | } 143 | 144 | lintOptions { 145 | abortOnError false 146 | disable 'MissingTranslation' 147 | } 148 | compileOptions { 149 | sourceCompatibility JavaVersion.VERSION_1_6 150 | } 151 | } 152 | 153 | gradle.projectsEvaluated { 154 | // NDK 155 | tasks.withType(NdkCompile) { 156 | // disable built-in NDK build 157 | compileTask -> compileTask.enabled = false 158 | } 159 | tasks.compileDebugSources.doLast { 160 | ndkBuild(true) 161 | } 162 | tasks.compileReleaseSources.doLast { 163 | ndkBuild(false) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /suhide/native/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | FLAGS := -std=c11 4 | LDLIBS := -ldl -llog 5 | 6 | #FLAGS += -DDEBUG 7 | 8 | include $(CLEAR_VARS) 9 | 10 | LOCAL_SRC_FILES := util.c trace.c config.c suhide.c 11 | 12 | LOCAL_MODULE := suhide64 13 | LOG_TAG := suhide64 14 | ifneq ($(TARGET_ARCH_ABI),arm64-v8a) 15 | ifneq ($(TARGET_ARCH_ABI),x86_64) 16 | ifneq ($(TARGET_ARCH_ABI),mips64) 17 | LOCAL_MODULE := suhide32 18 | LOG_TAG := suhide32 19 | endif 20 | endif 21 | endif 22 | 23 | LOCAL_CFLAGS := $(FLAGS) -DLOG_TAG=\"$(LOG_TAG)\" 24 | LOCAL_LDLIBS := $(LDLIBS) 25 | 26 | include $(BUILD_EXECUTABLE) 27 | 28 | include $(CLEAR_VARS) 29 | 30 | LOCAL_SRC_FILES := util.c getevent.c suhide_launcher.c 31 | 32 | LOCAL_MODULE := suhide 33 | LOG_TAG := suhide 34 | 35 | LOCAL_CFLAGS := $(FLAGS) -DLOG_TAG=\"$(LOG_TAG)\" 36 | LOCAL_LDLIBS := $(LDLIBS) 37 | 38 | include $(BUILD_EXECUTABLE) 39 | 40 | include $(CLEAR_VARS) 41 | 42 | LOCAL_SRC_FILES:= \ 43 | setpropex/setpropex.c \ 44 | setpropex/system_properties.c \ 45 | setpropex/system_properties_compat.c 46 | LOCAL_MODULE := setpropex 47 | LOCAL_CFLAGS += -std=c99 -I setpropex/inc -DNDEBUG 48 | LOCAL_LDLIBS := -llog -pie -fPIE 49 | 50 | include $(BUILD_EXECUTABLE) 51 | -------------------------------------------------------------------------------- /suhide/native/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM=android-21 2 | APP_ABI=all 3 | APP_PIE=true 4 | APP_CFLAGS := -Wall -Werror -fpic -O3 -fdata-sections -ffunction-sections -s -fvisibility=hidden -Wno-error=strict-aliasing 5 | #APP_CFLAGS := -g3 -ggdb -O0 6 | NDK_TOOLCHAIN_VERSION=4.9 7 | -------------------------------------------------------------------------------- /suhide/native/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "ndklog.h" 22 | 23 | #ifndef AID_USER 24 | #define AID_USER 100000 25 | #endif 26 | #ifndef AID_APP 27 | #define AID_APP 10000 28 | #endif 29 | 30 | #define UIDFILE "/sbin/supersu/suhide/suhide.uid" 31 | 32 | static uid_t* uids = NULL; 33 | static int uid_count = 0; 34 | 35 | static char** processes = NULL; 36 | static int process_count = 0; 37 | 38 | static time_t last_uid_time = 0; 39 | 40 | // load uids and process names root should be hidden from, checks last modification of config file 41 | void load_config() { 42 | struct stat stat; 43 | lstat(UIDFILE, &stat); 44 | if (stat.st_mtime == last_uid_time) return; 45 | last_uid_time = stat.st_mtime; 46 | 47 | int fd = open(UIDFILE, O_RDONLY); 48 | if (fd < 0) return; 49 | 50 | int buf_size = (int)stat.st_size; 51 | char buf[buf_size]; 52 | int buf_read = 0; 53 | 54 | while (1) { 55 | int r = read(fd, &buf[buf_read], buf_size - buf_read - 1); 56 | if (r > 0) { 57 | buf_read += r; 58 | } else { 59 | break; 60 | } 61 | } 62 | 63 | if (buf_read == 0) { 64 | close(fd); 65 | return; 66 | } 67 | 68 | buf[buf_read] = '\0'; 69 | buf_read++; 70 | 71 | if (uids != NULL) { 72 | free(uids); 73 | uids = NULL; 74 | } 75 | if (processes != NULL) { 76 | free(processes); 77 | processes = NULL; 78 | } 79 | int count_uid = 0; 80 | int count_process = 0; 81 | for (int loop = 0; loop < 2; loop++) { 82 | if (loop == 1) { 83 | uids = (uid_t*)malloc(sizeof(uid_t) * count_uid); 84 | uid_count = count_uid; 85 | count_uid = 0; 86 | 87 | processes = (char**)malloc(sizeof(char*) * count_process); 88 | process_count = count_process; 89 | count_process = 0; 90 | } 91 | 92 | int start = 0; 93 | for (int i = 0; i < buf_read; i++) { 94 | if ((buf[i] == '\r') || (buf[i] == '\n') || (buf[i] == ' ')) buf[i] = '\0'; 95 | if (buf[i] == '\0') { 96 | if ((start > -1) && (start < i - 1)) { 97 | uid_t uid = atoi(&buf[start]); 98 | if (uid > 0) { 99 | if (loop == 1) { 100 | LOGD("[%d] uid[%d]-->[%d]", getpid(), count_uid, uid); 101 | uids[count_uid] = uid; 102 | } 103 | count_uid++; 104 | } else { 105 | if (loop == 1) { 106 | LOGD("[%d] process[%d]-->[%s]", getpid(), count_process, &buf[start]); 107 | processes[count_process] = malloc(strlen(&buf[start]) + 1); 108 | strcpy(processes[count_process], &buf[start]); 109 | } 110 | count_process++; 111 | } 112 | } 113 | start = i + 1; 114 | } 115 | } 116 | } 117 | 118 | close(fd); 119 | } 120 | 121 | // is root access allowed for gid ? 122 | int allow_root_for_uid(gid_t gid) { 123 | if ((gid % AID_USER) < AID_APP) return 1; 124 | 125 | if (uid_count == 0) return 1; 126 | 127 | for (int i = 0; i < uid_count; i++) { 128 | if (uids[i] == gid) return 0; 129 | } 130 | 131 | return 1; 132 | } 133 | 134 | // is root access allowed for process name ? 135 | int allow_root_for_name(char* name) { 136 | if (process_count == 0) return 1; 137 | 138 | for (int i = 0; i < process_count; i++) { 139 | if (strcmp(processes[i], name) == 0) return 0; 140 | } 141 | 142 | return 1; 143 | } -------------------------------------------------------------------------------- /suhide/native/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _CONFIG_H 18 | #define _CONFIG_H 19 | 20 | void load_config(); 21 | int allow_root_for_uid(gid_t gid); 22 | int allow_root_for_name(char* name); 23 | 24 | #endif -------------------------------------------------------------------------------- /suhide/native/getevent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Loosely based on: http://androidxref.com/7.1.1_r6/xref/system/core/debuggerd/getevent.cpp 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | static struct pollfd* ufds = NULL; 32 | static int nfds = 0; 33 | 34 | // opens an input device, returns 0 on success 35 | static int open_device(const char* device) { 36 | int fd; 37 | struct pollfd* new_ufds; 38 | int version; 39 | struct input_id id; 40 | 41 | fd = open(device, O_RDWR); 42 | if (fd < 0) return -1; 43 | if (ioctl(fd, EVIOCGVERSION, &version)) return -1; 44 | if (ioctl(fd, EVIOCGID, &id)) return -1; 45 | 46 | new_ufds = (struct pollfd*)realloc(ufds, sizeof(ufds[0]) * (nfds + 1)); 47 | ufds = new_ufds; 48 | ufds[nfds].fd = fd; 49 | ufds[nfds].events = POLLIN; 50 | nfds++; 51 | 52 | return 0; 53 | } 54 | 55 | // scan a dir and open all contained input devices 56 | static int scan_dir(const char* dirname) { 57 | char devname[PATH_MAX]; 58 | char* filename; 59 | DIR* dir; 60 | struct dirent* de; 61 | dir = opendir(dirname); 62 | if (dir == NULL) 63 | return -1; 64 | strcpy(devname, dirname); 65 | filename = devname + strlen(devname); 66 | *filename++ = '/'; 67 | while ((de = readdir(dir))) { 68 | if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || 69 | (de->d_name[1] == '.' && de->d_name[2] == '\0')) 70 | continue; 71 | strcpy(filename, de->d_name); 72 | open_device(devname); 73 | } 74 | closedir(dir); 75 | return 0; 76 | } 77 | 78 | // (re-)init event monitoring 79 | void reset_getevent() { 80 | const char* device_path = "/dev/input"; 81 | if (ufds != NULL) { 82 | int i; 83 | for (i = 0; i < nfds; i++) { 84 | close(ufds[i].fd); 85 | } 86 | free(ufds); 87 | ufds = NULL; 88 | } 89 | nfds = 0; 90 | scan_dir(device_path); 91 | } 92 | 93 | // get most recent event with a timeout. returns number of events returned [0..1] or -1 on error 94 | int get_event(struct input_event* event, int timeout) { 95 | int res; 96 | int i; 97 | int pollres; 98 | while (1) { 99 | pollres = poll(ufds, nfds, timeout); 100 | if (pollres == 0) { 101 | return 0; 102 | } else if (pollres == -1) { 103 | return -1; 104 | } 105 | for (i = 0; i < nfds; i++) { 106 | if (ufds[i].revents) { 107 | if (ufds[i].revents & POLLIN) { 108 | res = read(ufds[i].fd, event, sizeof(*event)); 109 | if (res < (int)sizeof(event)) { 110 | return -1; 111 | } 112 | return 1; 113 | } 114 | } 115 | } 116 | } 117 | return 0; 118 | } -------------------------------------------------------------------------------- /suhide/native/getevent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _GETEVENT_H 18 | #define _GETEVENT_H 19 | 20 | #include 21 | 22 | void reset_getevent(); 23 | int get_event(struct input_event* event, int timeout); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /suhide/native/ndklog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Slightly modified cutils/log.h for use in NDK 18 | // 19 | // define LOG_TAG prior to inclusion 20 | // 21 | // if you define DEBUG or LOG_DEBUG before inclusion, VDIWE are logged 22 | // if LOG_SILENT is defined, nothing is logged (overrides (LOG_)DEBUG) 23 | // if none of the above are defined, VD are ignored and IWE are logged 24 | 25 | #ifndef _NDK_LOG_H 26 | #define _NDK_LOG_H 27 | 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #ifndef LOG_TAG 35 | #define LOG_TAG "NDK_LOG" 36 | #endif 37 | 38 | #ifdef DEBUG 39 | #define LOG_DEBUG 40 | #endif 41 | 42 | #ifndef LOG_SILENT 43 | #define LOG(...) __android_log_print(__VA_ARGS__) 44 | #else 45 | #define LOG(...) (void)0 46 | #endif 47 | 48 | #ifdef LOG_DEBUG 49 | #define LOGV(...) LOG(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 50 | #define LOGD(...) LOG(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 51 | #else 52 | #define LOGV(...) (void)0 53 | #define LOGD(...) (void)0 54 | #endif 55 | #define LOGI(...) LOG(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 56 | #define LOGW(...) LOG(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 57 | #define LOGE(...) LOG(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 58 | 59 | #define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) 60 | 61 | #define LOGV_IF(cond, ...) ((CONDITION(cond)) ? LOGV(__VA_ARGS__) : (void)0) 62 | #define LOGD_IF(cond, ...) ((CONDITION(cond)) ? LOGD(__VA_ARGS__) : (void)0) 63 | #define LOGI_IF(cond, ...) ((CONDITION(cond)) ? LOGI(__VA_ARGS__) : (void)0) 64 | #define LOGW_IF(cond, ...) ((CONDITION(cond)) ? LOGW(__VA_ARGS__) : (void)0) 65 | #define LOGE_IF(cond, ...) ((CONDITION(cond)) ? LOGE(__VA_ARGS__) : (void)0) 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif // _NDK_LOG_H -------------------------------------------------------------------------------- /suhide/native/setpropex/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | ifneq ($(TARGET_ARCH_ABI),arm64-v8a) 4 | ifneq ($(TARGET_ARCH_ABI),x86_64) 5 | ifneq ($(TARGET_ARCH_ABI),mips64) 6 | include $(CLEAR_VARS) 7 | LOCAL_SRC_FILES:= \ 8 | setpropex.c system_properties.c system_properties_compat.c 9 | LOCAL_MODULE := setpropex 10 | LOCAL_CFLAGS += -std=c99 -I jni/inc -DNDEBUG 11 | LOCAL_LDLIBS := -llog 12 | include $(BUILD_EXECUTABLE) 13 | endif 14 | endif 15 | endif 16 | 17 | include $(CLEAR_VARS) 18 | LOCAL_SRC_FILES:= \ 19 | setpropex.c system_properties.c system_properties_compat.c 20 | LOCAL_MODULE := setpropex-pie 21 | LOCAL_CFLAGS += -std=c99 -I jni/inc -DNDEBUG 22 | LOCAL_LDLIBS := -llog -pie -fPIE 23 | include $(BUILD_EXECUTABLE) 24 | -------------------------------------------------------------------------------- /suhide/native/setpropex/LICENSE: -------------------------------------------------------------------------------- 1 | setpropex taken from https://github.com/poliva/rootadb/tree/master/jni 2 | by Chainfire with permission from Pau Oliva -------------------------------------------------------------------------------- /suhide/native/setpropex/_system_properties.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in 12 | * the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _INCLUDE_SYS__SYSTEM_PROPERTIES_H 30 | #define _INCLUDE_SYS__SYSTEM_PROPERTIES_H 31 | 32 | #ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 33 | #error you should #include instead 34 | #else 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | 41 | /* 42 | * definitions for both versions 43 | */ 44 | #define PROP_AREA_MAGIC 0x504f5250 45 | #define PROP_AREA_VERSION 0xfc6ed0ab 46 | #define PROP_AREA_VERSION_COMPAT 0x45434f76 47 | 48 | extern bool compat_mode; 49 | 50 | extern size_t pa_size; 51 | extern size_t pa_data_size; 52 | 53 | 54 | /* 55 | * structures and definitions for >= kitkat 56 | */ 57 | 58 | struct prop_area { 59 | unsigned bytes_used; 60 | unsigned volatile serial; 61 | unsigned magic; 62 | unsigned version; 63 | unsigned reserved[28]; 64 | char data[0]; 65 | }; 66 | 67 | typedef struct prop_area prop_area; 68 | 69 | struct prop_info { 70 | unsigned volatile serial; 71 | char value[PROP_VALUE_MAX]; 72 | char name[0]; 73 | }; 74 | 75 | typedef struct prop_info prop_info; 76 | 77 | 78 | /* 79 | * structures and definitions for < kitkat 80 | */ 81 | 82 | struct prop_area_compat { 83 | unsigned volatile count; 84 | unsigned volatile serial; 85 | unsigned magic; 86 | unsigned version; 87 | unsigned reserved[4]; 88 | unsigned toc[1]; 89 | }; 90 | 91 | typedef struct prop_area_compat prop_area_compat; 92 | 93 | struct prop_info_compat { 94 | char name[PROP_NAME_MAX]; 95 | unsigned volatile serial; 96 | char value[PROP_VALUE_MAX]; 97 | }; 98 | 99 | typedef struct prop_info_compat prop_info_compat; 100 | 101 | const prop_info *__system_property_find_compat(const char *name); 102 | 103 | #endif 104 | 105 | #endif 106 | 107 | -------------------------------------------------------------------------------- /suhide/native/setpropex/inc/cutils/properties.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __CUTILS_PROPERTIES_H 18 | #define __CUTILS_PROPERTIES_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | /* System properties are *small* name value pairs managed by the 25 | ** property service. If your data doesn't fit in the provided 26 | ** space it is not appropriate for a system property. 27 | ** 28 | ** WARNING: system/bionic/include/sys/system_properties.h also defines 29 | ** these, but with different names. (TODO: fix that) 30 | */ 31 | #define PROPERTY_KEY_MAX 32 32 | #define PROPERTY_VALUE_MAX 92 33 | 34 | /* property_get: returns the length of the value which will never be 35 | ** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated. 36 | ** (the length does not include the terminating zero). 37 | ** 38 | ** If the property read fails or returns an empty value, the default 39 | ** value is used (if nonnull). 40 | */ 41 | int property_get(const char *key, char *value, const char *default_value); 42 | 43 | /* property_set: returns 0 on success, < 0 on failure 44 | */ 45 | int property_set(const char *key, const char *value); 46 | 47 | int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); 48 | 49 | 50 | #ifdef HAVE_SYSTEM_PROPERTY_SERVER 51 | /* 52 | * We have an external property server instead of built-in libc support. 53 | * Used by the simulator. 54 | */ 55 | #define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop" 56 | 57 | enum { 58 | kSystemPropertyUnknown = 0, 59 | kSystemPropertyGet, 60 | kSystemPropertySet, 61 | kSystemPropertyList 62 | }; 63 | #endif /*HAVE_SYSTEM_PROPERTY_SERVER*/ 64 | 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /suhide/native/setpropex/setpropex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* include the common system property implementation definitions */ 14 | #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 15 | #include "_system_properties.h" 16 | 17 | extern struct prop_area *__system_property_area__; 18 | 19 | #define LOG_TAG "setpropex" 20 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 21 | #ifndef NDEBUG 22 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) 23 | #else 24 | #define LOGD(...) 25 | #endif 26 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 27 | 28 | #include 29 | 30 | typedef struct mapinfo mapinfo; 31 | 32 | struct mapinfo { 33 | mapinfo *next; 34 | int pid; 35 | uintptr_t start; 36 | uintptr_t end; 37 | char perm[8]; 38 | char name[1024]; 39 | }; 40 | 41 | // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so 42 | // 7f8e960000-7f8e980000 rw-s 00000000 00:0e 2011 /dev/__properties__ 43 | // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 44 | // 0 1 2 3 4 5 6 7 45 | 46 | static int read_mapinfo(FILE *fp, mapinfo* mi) 47 | { 48 | char line[1024]; 49 | int len; 50 | 51 | skip: 52 | if(fgets(line, 1024, fp) == 0) return -1; 53 | 54 | len = strlen(line); 55 | if(len < 1) { 56 | LOGE("read_mapinfo: short line!"); 57 | return -1; 58 | } 59 | line[--len] = 0; 60 | 61 | if(mi == 0) { 62 | LOGE("read_mapinfo: NULL mapinfo!"); 63 | return -1; 64 | } 65 | 66 | char* end = strchr(line, '-') + 1; 67 | char* perm = strchr(line, ' ') + 1; 68 | if ((end == NULL) || (perm == NULL)) { 69 | LOGD("read_mapinfo: end/perm == NULL"); 70 | goto skip; 71 | } 72 | 73 | #ifdef __LP64__ 74 | mi->start = strtoull(line, 0, 16); 75 | mi->end = strtoull(end, 0, 16); 76 | #else 77 | mi->start = strtoul(line, 0, 16); 78 | mi->end = strtoul(end, 0, 16); 79 | #endif 80 | strncpy(mi->perm, perm, 4); 81 | 82 | if(len < 50) { 83 | LOGD("read_mapinfo: line too short! (%d bytes) skipping", len); 84 | //mi->name[0] = '\0'; 85 | goto skip; 86 | } else { 87 | #ifdef __LP64__ 88 | strcpy(mi->name, line + 73); 89 | #else 90 | strcpy(mi->name, line + 49); 91 | #endif 92 | } 93 | 94 | return 0; 95 | } 96 | 97 | static mapinfo *load_maps(int pid) 98 | { 99 | char tmp[128]; 100 | FILE *fp; 101 | mapinfo *mi, *last, *ret; 102 | 103 | sprintf(tmp, "/proc/%d/maps", pid); 104 | fp = fopen(tmp, "r"); 105 | if(fp == 0) { 106 | LOGE("load_maps: unable to open maps file: %s", strerror(errno)); 107 | return NULL; 108 | } 109 | 110 | ret = NULL; 111 | last = NULL; 112 | mi = malloc(sizeof(mapinfo)); 113 | memset(mi, 0, sizeof(mapinfo)); 114 | while(!read_mapinfo(fp, mi)) { 115 | LOGD("load_maps: %"PRIxPTR" %s %s", mi->start, mi->perm, mi->name); 116 | mi->pid = pid; 117 | if (ret == NULL) ret = mi; 118 | if (last != NULL) last->next = mi; 119 | last = mi; 120 | mi = malloc(sizeof(mapinfo)); 121 | memset(mi, 0, sizeof(mapinfo)); 122 | } 123 | 124 | fclose(fp); 125 | free(mi); 126 | return ret; 127 | } 128 | 129 | static void dump_region(int fd, uintptr_t start, uintptr_t end, char* mem) 130 | { 131 | lseek64(fd, start, SEEK_SET); 132 | while(start < end) { 133 | int rd; 134 | int len; 135 | 136 | len = end - start; 137 | if(len > 4096) 138 | len = 4096; 139 | rd = read(fd, mem, len); 140 | if (rd != len) 141 | LOGE("dump_region: short read!"); 142 | //write(1, mem, len); 143 | start += 4096; 144 | mem += 4096; 145 | } 146 | } 147 | 148 | 149 | /* set it in init's memory */ 150 | static void update_init(mapinfo *mi, void *pa, void *pi, size_t len) 151 | { 152 | uintptr_t offset = (pi - pa); 153 | uintptr_t *addr = (uintptr_t*)(mi->start + offset); 154 | uintptr_t *data = (uintptr_t*)pi; 155 | int ret; 156 | 157 | LOGD("write %"PRIxPTR, mi->start + offset); 158 | for (int i = 0; i < len / 4; i++) { 159 | ret = ptrace(PTRACE_POKEDATA, mi->pid, (void*)(addr + i), (void*)data[i]); 160 | if (ret != 0) { 161 | LOGE("ptrace POKEDATA: %s", strerror(errno)); 162 | break; 163 | } 164 | } 165 | } 166 | 167 | 168 | static void update_prop_info(mapinfo *mi, void *pa, prop_info *pi, const char *value, unsigned len) 169 | { 170 | LOGD("new/before: pi=%p name=%s value=%s", pi, pi->name, pi->value); 171 | pi->serial = pi->serial | 1; 172 | memcpy(pi->value, value, len + 1); 173 | pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); 174 | LOGD("new/after: pi=%p name=%s value=%s", pi, pi->name, pi->value); 175 | 176 | /* update init */ 177 | update_init(mi, pa, pi, sizeof(*pi)); 178 | } 179 | 180 | static void update_prop_info_compat(mapinfo *mi, void *pa, prop_info_compat *pi, const char *value, unsigned len) 181 | { 182 | LOGD("old/before: pi=%p name=%s value=%s", pi, pi->name, pi->value); 183 | pi->serial = pi->serial | 1; 184 | memcpy(pi->value, value, len + 1); 185 | pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); 186 | LOGD("old/after: pi=%p name=%s value=%s", pi, pi->name, pi->value); 187 | 188 | /* update init */ 189 | update_init(mi, pa, pi, sizeof(*pi)); 190 | } 191 | 192 | /* first, set it in our memory, then in init'd memory */ 193 | static int property_set_ex(const char *name, const char *value, int mem, mapinfo *mi) 194 | { 195 | void *pi; 196 | 197 | int namelen = strlen(name); 198 | int valuelen = strlen(value); 199 | 200 | if(namelen >= PROP_NAME_MAX) { 201 | LOGE("name too long!"); 202 | return -1; 203 | } 204 | if(valuelen >= PROP_VALUE_MAX) { 205 | LOGE("value too long!"); 206 | return -1; 207 | } 208 | if(namelen < 1) { 209 | LOGE("name too short!"); 210 | return -1; 211 | } 212 | 213 | pi = (void *)__system_property_find(name); 214 | 215 | if(pi != 0) { 216 | char *pa = (char *)__system_property_area__; 217 | 218 | if (compat_mode) 219 | update_prop_info_compat(mi, pa, pi, value, valuelen); 220 | else 221 | update_prop_info(mi, pa, pi, value, valuelen); 222 | 223 | return 0; 224 | } 225 | 226 | return -1; 227 | } 228 | 229 | static int setpropex(int init_pid, int argc, char *argv[]) 230 | { 231 | int ret = 0; 232 | char tmp[128]; 233 | mapinfo *mi, *mj; 234 | 235 | if (!((argc >= 3) && (argc % 2 == 1))) { 236 | fprintf(stderr, "usage: setpropex [ [...]] []\n"); 237 | return 1; 238 | } 239 | 240 | /* open it up, so we can read a copy */ 241 | sprintf(tmp, "/proc/%d/mem", init_pid); 242 | int mem = open(tmp, O_RDONLY); 243 | if(mem == -1) { 244 | LOGE("unable to open init's mem: %s", strerror(errno)); 245 | goto error2; 246 | } 247 | 248 | mj = load_maps(init_pid); 249 | int argi = 1; 250 | while (argi < argc) { 251 | int lret = -1; 252 | mi = mj; 253 | while(mi != NULL) { 254 | if ( 255 | /* try several different strategies to find the property area in init */ 256 | (!strcmp(mi->perm, "rw-s") && !strcmp(mi->name, "/dev/__properties__")) || 257 | (!strcmp(mi->perm, "rw-s") && !strcmp(mi->name, "/dev/__properties__ (deleted)")) || 258 | (!strcmp(mi->perm, "rwxs") && !strcmp(mi->name, "/dev/__properties__ (deleted)")) || 259 | (!strcmp(mi->perm, "rwxs") && !strcmp(mi->name, "/dev/ashmem/system_properties (deleted)")) || 260 | 261 | /* property spaces split per SELinux type */ 262 | (!strcmp(mi->perm, "rw-s") && !strncmp(mi->name, "/dev/__properties__/u", strlen("/dev/__properties__/u"))) 263 | ) { 264 | LOGD("map found @ %"PRIxPTR" %"PRIxPTR" %s %s", mi->start, mi->end, mi->perm, mi->name); 265 | 266 | pa_size = mi->end - mi->start; 267 | pa_data_size = pa_size - sizeof(prop_area); 268 | 269 | /* allocate memory for our copy */ 270 | void *p = malloc(pa_size); 271 | if(p == 0) { 272 | LOGE("unable to allocate memory for our copy of the property area"); 273 | goto error1; 274 | } 275 | 276 | /* got it, read the data and set it for the system_property code */ 277 | dump_region(mem, mi->start, mi->end, p); 278 | __system_property_area__ = p; 279 | 280 | /* detect old versions of android and use the correct implementation 281 | * accordingly */ 282 | if (__system_property_area__->version == PROP_AREA_VERSION_COMPAT) 283 | compat_mode = true; 284 | 285 | /* set the property */ 286 | 287 | lret = property_set_ex(argv[argi], argv[argi + 1], mem, mi); 288 | 289 | /* clean up */ 290 | free(p); 291 | 292 | if (lret == 0) 293 | break; 294 | } 295 | 296 | mi = mi->next; 297 | } 298 | 299 | if (lret == -1) { 300 | LOGE("not found: %s", argv[argi]); 301 | ret = -1; 302 | } 303 | 304 | argi += 2; 305 | } 306 | 307 | error1: 308 | close(mem); 309 | error2: 310 | if(ret == 0){ 311 | return 0; 312 | } 313 | 314 | return 1; 315 | } 316 | 317 | static int lock_file(char* filename) { 318 | int fd = open(filename, O_CREAT | O_RDONLY, 0644); 319 | if (fd < 0) { 320 | LOGE("open failed!"); 321 | return 1; 322 | } 323 | if (flock(fd, LOCK_EX) != 0) { 324 | LOGE("lock failed!"); 325 | close(fd); 326 | return 1; 327 | } 328 | return fd; 329 | } 330 | 331 | static void unlock_file(int fd) { 332 | flock(fd, LOCK_UN); 333 | close(fd); 334 | } 335 | 336 | int main(int argc, char** argv) 337 | { 338 | int init_pid = 1; //TODO: find init process 339 | 340 | int lock = -1; 341 | if ((argc >= 4) && (argc % 2 == 0)) { 342 | lock = lock_file(argv[argc - 1]); 343 | argc--; 344 | } 345 | 346 | // we need this even with locking, if called rapidly 347 | for (int i = 127; i >= 0; i--) { 348 | if (ptrace(PTRACE_ATTACH, init_pid, NULL, NULL) == -1) { 349 | if (i == 0) { 350 | LOGE("ptrace error: failed to attach to %d, %s", init_pid, strerror(errno)); 351 | return 1; 352 | } else { 353 | usleep(1000); 354 | } 355 | } else { 356 | break; 357 | } 358 | } 359 | 360 | int ret = setpropex(init_pid, argc, argv); 361 | 362 | ptrace(PTRACE_DETACH, 1, NULL, NULL); 363 | kill(init_pid, SIGCONT); 364 | kill(init_pid, SIGCONT); // yes, twice 365 | 366 | if (lock >= 0) unlock_file(lock); 367 | 368 | return ret; 369 | } 370 | -------------------------------------------------------------------------------- /suhide/native/setpropex/system_properties.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in 12 | * the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | 29 | #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 30 | #include "_system_properties.h" 31 | 32 | #if 0 33 | #include 34 | #include 35 | #endif 36 | #include 37 | #if 0 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #endif 44 | #include 45 | #if 0 46 | #include 47 | 48 | #include 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | #endif 55 | #include 56 | #if 0 57 | #include 58 | #include 59 | 60 | #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 61 | #include 62 | 63 | #include 64 | #include 65 | 66 | 67 | #define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) 68 | 69 | #endif 70 | 71 | /* 72 | * Properties are stored in a hybrid trie/binary tree structure. 73 | * Each property's name is delimited at '.' characters, and the tokens are put 74 | * into a trie structure. Siblings at each level of the trie are stored in a 75 | * binary tree. For instance, "ro.secure"="1" could be stored as follows: 76 | * 77 | * +-----+ children +----+ children +--------+ 78 | * | |-------------->| ro |-------------->| secure | 79 | * +-----+ +----+ +--------+ 80 | * / \ / | 81 | * left / \ right left / | prop +===========+ 82 | * v v v +-------->| ro.secure | 83 | * +-----+ +-----+ +-----+ +-----------+ 84 | * | net | | sys | | com | | 1 | 85 | * +-----+ +-----+ +-----+ +===========+ 86 | */ 87 | 88 | typedef volatile uint32_t prop_off_t; 89 | struct prop_bt { 90 | uint8_t namelen; 91 | uint8_t reserved[3]; 92 | 93 | prop_off_t prop; 94 | 95 | prop_off_t left; 96 | prop_off_t right; 97 | 98 | prop_off_t children; 99 | 100 | char name[0]; 101 | }; 102 | 103 | typedef struct prop_bt prop_bt; 104 | 105 | #if 0 106 | static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; 107 | static char property_filename[PATH_MAX] = PROP_FILENAME; 108 | #endif 109 | bool compat_mode = false; 110 | prop_area *__system_property_area__ = NULL; 111 | 112 | size_t pa_data_size; 113 | size_t pa_size; 114 | 115 | #if 0 116 | static int get_fd_from_env(void) 117 | { 118 | char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); 119 | 120 | if (!env) { 121 | return -1; 122 | } 123 | 124 | return atoi(env); 125 | } 126 | 127 | static int map_prop_area_rw() 128 | { 129 | prop_area *pa; 130 | int fd; 131 | int ret; 132 | 133 | /* dev is a tmpfs that we can use to carve a shared workspace 134 | * out of, so let's do that... 135 | */ 136 | fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | 137 | O_EXCL, 0444); 138 | if (fd < 0) { 139 | if (errno == EACCES) { 140 | /* for consistency with the case where the process has already 141 | * mapped the page in and segfaults when trying to write to it 142 | */ 143 | abort(); 144 | } 145 | return -1; 146 | } 147 | 148 | ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 149 | if (ret < 0) 150 | goto out; 151 | 152 | if (ftruncate(fd, PA_SIZE) < 0) 153 | goto out; 154 | 155 | pa_size = PA_SIZE; 156 | pa_data_size = pa_size - sizeof(prop_area); 157 | compat_mode = false; 158 | 159 | pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 160 | if(pa == MAP_FAILED) 161 | goto out; 162 | 163 | memset(pa, 0, pa_size); 164 | pa->magic = PROP_AREA_MAGIC; 165 | pa->version = PROP_AREA_VERSION; 166 | /* reserve root node */ 167 | pa->bytes_used = sizeof(prop_bt); 168 | 169 | /* plug into the lib property services */ 170 | __system_property_area__ = pa; 171 | 172 | close(fd); 173 | return 0; 174 | 175 | out: 176 | close(fd); 177 | return -1; 178 | } 179 | 180 | int __system_property_set_filename(const char *filename) 181 | { 182 | size_t len = strlen(filename); 183 | if (len >= sizeof(property_filename)) 184 | return -1; 185 | 186 | strcpy(property_filename, filename); 187 | return 0; 188 | } 189 | 190 | int __system_property_area_init() 191 | { 192 | return map_prop_area_rw(); 193 | } 194 | 195 | static int map_prop_area() 196 | { 197 | bool fromFile = true; 198 | int result = -1; 199 | int fd; 200 | int ret; 201 | 202 | fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); 203 | if (fd >= 0) { 204 | /* For old kernels that don't support O_CLOEXEC */ 205 | ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 206 | if (ret < 0) 207 | goto cleanup; 208 | } 209 | 210 | if ((fd < 0) && (errno == ENOENT)) { 211 | /* 212 | * For backwards compatibility, if the file doesn't 213 | * exist, we use the environment to get the file descriptor. 214 | * For security reasons, we only use this backup if the kernel 215 | * returns ENOENT. We don't want to use the backup if the kernel 216 | * returns other errors such as ENOMEM or ENFILE, since it 217 | * might be possible for an external program to trigger this 218 | * condition. 219 | */ 220 | fd = get_fd_from_env(); 221 | fromFile = false; 222 | } 223 | 224 | if (fd < 0) { 225 | return -1; 226 | } 227 | 228 | struct stat fd_stat; 229 | if (fstat(fd, &fd_stat) < 0) { 230 | goto cleanup; 231 | } 232 | 233 | if ((fd_stat.st_uid != 0) 234 | || (fd_stat.st_gid != 0) 235 | || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) 236 | || (fd_stat.st_size < sizeof(prop_area)) ) { 237 | goto cleanup; 238 | } 239 | 240 | pa_size = fd_stat.st_size; 241 | pa_data_size = pa_size - sizeof(prop_area); 242 | prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); 243 | 244 | if (pa == MAP_FAILED) { 245 | goto cleanup; 246 | } 247 | 248 | if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION && 249 | pa->version != PROP_AREA_VERSION_COMPAT)) { 250 | munmap(pa, pa_size); 251 | goto cleanup; 252 | } 253 | 254 | if (pa->version == PROP_AREA_VERSION_COMPAT) { 255 | compat_mode = true; 256 | } 257 | 258 | result = 0; 259 | 260 | __system_property_area__ = pa; 261 | 262 | cleanup: 263 | if (fromFile) { 264 | close(fd); 265 | } 266 | 267 | return result; 268 | } 269 | 270 | int __system_properties_init() 271 | { 272 | return map_prop_area(); 273 | } 274 | 275 | static void *new_prop_obj(size_t size, prop_off_t *off) 276 | { 277 | prop_area *pa = __system_property_area__; 278 | size = ALIGN(size, sizeof(uint32_t)); 279 | 280 | if (pa->bytes_used + size > pa_data_size) 281 | return NULL; 282 | 283 | *off = pa->bytes_used; 284 | __system_property_area__->bytes_used += size; 285 | return __system_property_area__->data + *off; 286 | } 287 | 288 | static prop_bt *new_prop_bt(const char *name, uint8_t namelen, prop_off_t *off) 289 | { 290 | prop_off_t off_tmp; 291 | prop_bt *bt = new_prop_obj(sizeof(prop_bt) + namelen + 1, &off_tmp); 292 | if (bt) { 293 | memcpy(bt->name, name, namelen); 294 | bt->name[namelen] = '\0'; 295 | bt->namelen = namelen; 296 | ANDROID_MEMBAR_FULL(); 297 | *off = off_tmp; 298 | } 299 | 300 | return bt; 301 | } 302 | 303 | static prop_info *new_prop_info(const char *name, uint8_t namelen, 304 | const char *value, uint8_t valuelen, prop_off_t *off) 305 | { 306 | prop_off_t off_tmp; 307 | prop_info *info = new_prop_obj(sizeof(prop_info) + namelen + 1, &off_tmp); 308 | if (info) { 309 | memcpy(info->name, name, namelen); 310 | info->name[namelen] = '\0'; 311 | info->serial = (valuelen << 24); 312 | memcpy(info->value, value, valuelen); 313 | info->value[valuelen] = '\0'; 314 | ANDROID_MEMBAR_FULL(); 315 | *off = off_tmp; 316 | } 317 | 318 | return info; 319 | } 320 | #endif 321 | 322 | static void *to_prop_obj(prop_off_t off) 323 | { 324 | if (off > pa_data_size) 325 | return NULL; 326 | 327 | return __system_property_area__->data + off; 328 | } 329 | 330 | static prop_bt *root_node() 331 | { 332 | return to_prop_obj(0); 333 | } 334 | 335 | static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, 336 | uint8_t two_len) 337 | { 338 | if (one_len < two_len) 339 | return -1; 340 | else if (one_len > two_len) 341 | return 1; 342 | else 343 | return strncmp(one, two, one_len); 344 | } 345 | 346 | static prop_bt *find_prop_bt(prop_bt *bt, const char *name, uint8_t namelen, 347 | bool alloc_if_needed) 348 | { 349 | while (true) { 350 | int ret; 351 | if (!bt) 352 | return bt; 353 | ret = cmp_prop_name(name, namelen, bt->name, bt->namelen); 354 | 355 | if (ret == 0) { 356 | return bt; 357 | } else if (ret < 0) { 358 | if (bt->left) { 359 | bt = to_prop_obj(bt->left); 360 | } else { 361 | //if (!alloc_if_needed) 362 | return NULL; 363 | 364 | //bt = new_prop_bt(name, namelen, &bt->left); 365 | } 366 | } else { 367 | if (bt->right) { 368 | bt = to_prop_obj(bt->right); 369 | } else { 370 | //if (!alloc_if_needed) 371 | return NULL; 372 | 373 | //bt = new_prop_bt(name, namelen, &bt->right); 374 | } 375 | } 376 | } 377 | } 378 | 379 | static const prop_info *find_property(prop_bt *trie, const char *name, 380 | uint8_t namelen, const char *value, uint8_t valuelen, 381 | bool alloc_if_needed) 382 | { 383 | const char *remaining_name = name; 384 | 385 | while (true) { 386 | char *sep = strchr(remaining_name, '.'); 387 | bool want_subtree = (sep != NULL); 388 | uint8_t substr_size; 389 | 390 | prop_bt *root; 391 | 392 | if (want_subtree) { 393 | substr_size = sep - remaining_name; 394 | } else { 395 | substr_size = strlen(remaining_name); 396 | } 397 | 398 | if (!substr_size) 399 | return NULL; 400 | 401 | if (trie->children) { 402 | root = to_prop_obj(trie->children); 403 | #if 0 404 | } else if (alloc_if_needed) { 405 | root = new_prop_bt(remaining_name, substr_size, &trie->children); 406 | #endif 407 | } else { 408 | root = NULL; 409 | } 410 | 411 | if (!root) 412 | return NULL; 413 | 414 | trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); 415 | if (!trie) 416 | return NULL; 417 | 418 | if (!want_subtree) 419 | break; 420 | 421 | remaining_name = sep + 1; 422 | } 423 | 424 | if (trie->prop) { 425 | return to_prop_obj(trie->prop); 426 | #if 0 427 | } else if (alloc_if_needed) { 428 | return new_prop_info(name, namelen, value, valuelen, &trie->prop); 429 | #endif 430 | } else { 431 | return NULL; 432 | } 433 | } 434 | 435 | const prop_info *__system_property_find(const char *name) 436 | { 437 | if (__predict_false(compat_mode)) { 438 | return __system_property_find_compat(name); 439 | } 440 | return find_property(root_node(), name, strlen(name), NULL, 0, false); 441 | } 442 | 443 | #if 0 444 | int __system_property_read(const prop_info *pi, char *name, char *value) 445 | { 446 | unsigned serial, len; 447 | 448 | if (__predict_false(compat_mode)) { 449 | return __system_property_read_compat(pi, name, value); 450 | } 451 | 452 | for(;;) { 453 | serial = pi->serial; 454 | while(SERIAL_DIRTY(serial)) { 455 | __futex_wait((volatile void *)&pi->serial, serial, 0); 456 | serial = pi->serial; 457 | } 458 | len = SERIAL_VALUE_LEN(serial); 459 | memcpy(value, pi->value, len + 1); 460 | ANDROID_MEMBAR_FULL(); 461 | if(serial == pi->serial) { 462 | if(name != 0) { 463 | strcpy(name, pi->name); 464 | } 465 | return len; 466 | } 467 | } 468 | } 469 | 470 | int __system_property_get(const char *name, char *value) 471 | { 472 | const prop_info *pi = __system_property_find(name); 473 | 474 | if(pi != 0) { 475 | return __system_property_read(pi, 0, value); 476 | } else { 477 | value[0] = 0; 478 | return 0; 479 | } 480 | } 481 | 482 | 483 | static int send_prop_msg(prop_msg *msg) 484 | { 485 | struct pollfd pollfds[1]; 486 | struct sockaddr_un addr; 487 | socklen_t alen; 488 | size_t namelen; 489 | int s; 490 | int r; 491 | int result = -1; 492 | 493 | s = socket(AF_LOCAL, SOCK_STREAM, 0); 494 | if(s < 0) { 495 | return result; 496 | } 497 | 498 | memset(&addr, 0, sizeof(addr)); 499 | namelen = strlen(property_service_socket); 500 | strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path); 501 | addr.sun_family = AF_LOCAL; 502 | alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; 503 | 504 | if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) { 505 | close(s); 506 | return result; 507 | } 508 | 509 | r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0)); 510 | 511 | if(r == sizeof(prop_msg)) { 512 | // We successfully wrote to the property server but now we 513 | // wait for the property server to finish its work. It 514 | // acknowledges its completion by closing the socket so we 515 | // poll here (on nothing), waiting for the socket to close. 516 | // If you 'adb shell setprop foo bar' you'll see the POLLHUP 517 | // once the socket closes. Out of paranoia we cap our poll 518 | // at 250 ms. 519 | pollfds[0].fd = s; 520 | pollfds[0].events = 0; 521 | r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); 522 | if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) { 523 | result = 0; 524 | } else { 525 | // Ignore the timeout and treat it like a success anyway. 526 | // The init process is single-threaded and its property 527 | // service is sometimes slow to respond (perhaps it's off 528 | // starting a child process or something) and thus this 529 | // times out and the caller thinks it failed, even though 530 | // it's still getting around to it. So we fake it here, 531 | // mostly for ctl.* properties, but we do try and wait 250 532 | // ms so callers who do read-after-write can reliably see 533 | // what they've written. Most of the time. 534 | // TODO: fix the system properties design. 535 | result = 0; 536 | } 537 | } 538 | 539 | close(s); 540 | return result; 541 | } 542 | 543 | int __system_property_set(const char *key, const char *value) 544 | { 545 | int err; 546 | prop_msg msg; 547 | 548 | if(key == 0) return -1; 549 | if(value == 0) value = ""; 550 | if(strlen(key) >= PROP_NAME_MAX) return -1; 551 | if(strlen(value) >= PROP_VALUE_MAX) return -1; 552 | 553 | memset(&msg, 0, sizeof msg); 554 | msg.cmd = PROP_MSG_SETPROP; 555 | strlcpy(msg.name, key, sizeof msg.name); 556 | strlcpy(msg.value, value, sizeof msg.value); 557 | 558 | err = send_prop_msg(&msg); 559 | if(err < 0) { 560 | return err; 561 | } 562 | 563 | return 0; 564 | } 565 | 566 | int __system_property_wait(const prop_info *pi) 567 | { 568 | unsigned n; 569 | if(pi == 0) { 570 | prop_area *pa = __system_property_area__; 571 | n = pa->serial; 572 | do { 573 | __futex_wait(&pa->serial, n, 0); 574 | } while(n == pa->serial); 575 | } else { 576 | n = pi->serial; 577 | do { 578 | __futex_wait((volatile void *)&pi->serial, n, 0); 579 | } while(n == pi->serial); 580 | } 581 | return 0; 582 | } 583 | 584 | int __system_property_update(prop_info *pi, const char *value, unsigned int len) 585 | { 586 | prop_area *pa = __system_property_area__; 587 | 588 | if (len >= PROP_VALUE_MAX) 589 | return -1; 590 | 591 | pi->serial = pi->serial | 1; 592 | ANDROID_MEMBAR_FULL(); 593 | memcpy(pi->value, value, len + 1); 594 | ANDROID_MEMBAR_FULL(); 595 | pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); 596 | __futex_wake(&pi->serial, INT32_MAX); 597 | 598 | pa->serial++; 599 | __futex_wake(&pa->serial, INT32_MAX); 600 | 601 | return 0; 602 | } 603 | 604 | int __system_property_add(const char *name, unsigned int namelen, 605 | const char *value, unsigned int valuelen) 606 | { 607 | prop_area *pa = __system_property_area__; 608 | const prop_info *pi; 609 | 610 | if (namelen >= PROP_NAME_MAX) 611 | return -1; 612 | if (valuelen >= PROP_VALUE_MAX) 613 | return -1; 614 | if (namelen < 1) 615 | return -1; 616 | 617 | pi = find_property(root_node(), name, namelen, value, valuelen, true); 618 | if (!pi) 619 | return -1; 620 | 621 | pa->serial++; 622 | __futex_wake(&pa->serial, INT32_MAX); 623 | return 0; 624 | } 625 | 626 | unsigned int __system_property_serial(const prop_info *pi) 627 | { 628 | return pi->serial; 629 | } 630 | 631 | unsigned int __system_property_wait_any(unsigned int serial) 632 | { 633 | prop_area *pa = __system_property_area__; 634 | 635 | do { 636 | __futex_wait(&pa->serial, serial, 0); 637 | } while(pa->serial == serial); 638 | 639 | return pa->serial; 640 | } 641 | 642 | struct find_nth_cookie { 643 | unsigned count; 644 | unsigned n; 645 | const prop_info *pi; 646 | }; 647 | 648 | static void find_nth_fn(const prop_info *pi, void *ptr) 649 | { 650 | struct find_nth_cookie *cookie = ptr; 651 | 652 | if (cookie->n == cookie->count) 653 | cookie->pi = pi; 654 | 655 | cookie->count++; 656 | } 657 | 658 | const prop_info *__system_property_find_nth(unsigned n) 659 | { 660 | struct find_nth_cookie cookie; 661 | int err; 662 | 663 | memset(&cookie, 0, sizeof(cookie)); 664 | cookie.n = n; 665 | 666 | err = __system_property_foreach(find_nth_fn, &cookie); 667 | if (err < 0) 668 | return NULL; 669 | 670 | return cookie.pi; 671 | } 672 | 673 | static int foreach_property(prop_off_t off, 674 | void (*propfn)(const prop_info *pi, void *cookie), void *cookie) 675 | { 676 | prop_bt *trie = to_prop_obj(off); 677 | if (!trie) 678 | return -1; 679 | 680 | if (trie->left) { 681 | int err = foreach_property(trie->left, propfn, cookie); 682 | if (err < 0) 683 | return -1; 684 | } 685 | if (trie->prop) { 686 | prop_info *info = to_prop_obj(trie->prop); 687 | if (!info) 688 | return -1; 689 | propfn(info, cookie); 690 | } 691 | if (trie->children) { 692 | int err = foreach_property(trie->children, propfn, cookie); 693 | if (err < 0) 694 | return -1; 695 | } 696 | if (trie->right) { 697 | int err = foreach_property(trie->right, propfn, cookie); 698 | if (err < 0) 699 | return -1; 700 | } 701 | 702 | return 0; 703 | } 704 | 705 | int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), 706 | void *cookie) 707 | { 708 | if (__predict_false(compat_mode)) { 709 | return __system_property_foreach_compat(propfn, cookie); 710 | } 711 | return foreach_property(0, propfn, cookie); 712 | } 713 | 714 | #endif 715 | 716 | -------------------------------------------------------------------------------- /suhide/native/setpropex/system_properties_compat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in 12 | * the documentation and/or other materials provided with the 13 | * distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | 29 | /* 30 | * This file is only used to provide backwards compatibility to property areas 31 | * created by old versions of init, which occurs when an ota runs. The updater 32 | * binary is compiled statically against the newest bionic, but the recovery 33 | * ramdisk may be using an old version of init. This can all be removed once 34 | * OTAs from pre-K versions are no longer supported. 35 | */ 36 | 37 | #include 38 | #if 0 39 | #include 40 | #endif 41 | 42 | #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 43 | #include "_system_properties.h" 44 | 45 | #define TOC_NAME_LEN(toc) ((toc) >> 24) 46 | #define TOC_TO_INFO(area, toc) ((prop_info_compat*) (((char*) area) + ((toc) & 0xFFFFFF))) 47 | 48 | 49 | extern prop_area *__system_property_area__; 50 | 51 | const prop_info *__system_property_find_compat(const char *name) 52 | { 53 | prop_area_compat *pa = (prop_area_compat *)__system_property_area__; 54 | unsigned count = pa->count; 55 | unsigned *toc = pa->toc; 56 | unsigned len = strlen(name); 57 | prop_info_compat *pi; 58 | 59 | if (len >= PROP_NAME_MAX) 60 | return 0; 61 | if (len < 1) 62 | return 0; 63 | 64 | while(count--) { 65 | unsigned entry = *toc++; 66 | if(TOC_NAME_LEN(entry) != len) continue; 67 | 68 | pi = TOC_TO_INFO(pa, entry); 69 | if(memcmp(name, pi->name, len)) continue; 70 | 71 | return (const prop_info *)pi; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | #if 0 78 | int __system_property_read_compat(const prop_info *_pi, char *name, char *value) 79 | { 80 | unsigned serial, len; 81 | const prop_info_compat *pi = (const prop_info_compat *)_pi; 82 | 83 | for(;;) { 84 | serial = pi->serial; 85 | while(SERIAL_DIRTY(serial)) { 86 | __futex_wait((volatile void *)&pi->serial, serial, 0); 87 | serial = pi->serial; 88 | } 89 | len = SERIAL_VALUE_LEN(serial); 90 | memcpy(value, pi->value, len + 1); 91 | if(serial == pi->serial) { 92 | if(name != 0) { 93 | strcpy(name, pi->name); 94 | } 95 | return len; 96 | } 97 | } 98 | } 99 | 100 | int __system_property_foreach_compat( 101 | void (*propfn)(const prop_info *pi, void *cookie), 102 | void *cookie) 103 | { 104 | prop_area_compat *pa = (prop_area_compat *)__system_property_area__; 105 | unsigned i; 106 | 107 | for (i = 0; i < pa->count; i++) { 108 | unsigned entry = pa->toc[i]; 109 | prop_info_compat *pi = TOC_TO_INFO(pa, entry); 110 | propfn((const prop_info *)pi, cookie); 111 | } 112 | 113 | return 0; 114 | } 115 | #endif 116 | 117 | -------------------------------------------------------------------------------- /suhide/native/suhide.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* Main file for suhide[32|64]. This process attaches to zygote(64) and monitors forks and 18 | * clones. As soon as an app forks from zygote it checks if that app should have root or not, 19 | * and if necessary unmounts all root-related mounts. 20 | * 21 | * All of this is based around PTRACE, though it is not architecture-specific. PTRACE is 22 | * tricky to work with, and there are many odd edge-cases. The current code has been 23 | * extensively tested to work as expected, don't touch it unless you're absolutely 24 | * sure you know what you're doing. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "ndklog.h" 38 | #include "util.h" 39 | #include "trace.h" 40 | #include "config.h" 41 | 42 | // unmount all root-related mounts from pid; done by forking a child which enters the target's 43 | // namespace, enumerates mounts, and unmounts the non-standard ones 44 | static void unmount_root(char* name, pid_t zygote, pid_t pid) { 45 | pid_t child = fork(); 46 | if (child == 0) { 47 | // child 48 | char path1[PATH_MAX]; 49 | char path2[PATH_MAX]; 50 | char ns1[PATH_MAX]; 51 | char ns2[PATH_MAX]; 52 | snprintf(path1, PATH_MAX, "/proc/%d/ns/mnt", zygote); 53 | snprintf(path2, PATH_MAX, "/proc/%d/ns/mnt", pid); 54 | ssize_t len1 = readlink(path1, ns1, PATH_MAX); 55 | ssize_t len2 = readlink(path2, ns2, PATH_MAX); 56 | if ((len1 > 0) && (len2 > 0)) { 57 | ns1[len1] = '\0'; 58 | ns2[len2] = '\0'; 59 | if (strcmp(ns1, ns2) != 0) { 60 | // we truly have different namespaces 61 | int nsfd = open(path2, O_RDONLY); 62 | if (nsfd >= 0) { 63 | if (syscall(__NR_setns, nsfd, CLONE_NEWNS) == 0) { 64 | // read mounts 65 | 66 | int fd = open("/proc/self/mountinfo", O_RDONLY); 67 | if (fd >= 0) { 68 | char buf[32768]; 69 | int total = 0; 70 | int size = 32768; 71 | while (1) { 72 | int r = read(fd, &buf[total], size - total); 73 | if (r <= 0) break; 74 | total += r; 75 | } 76 | close(fd); 77 | 78 | if (total > 0) { 79 | char* start = buf; 80 | for (int i = 0; i < total; i++) { 81 | if (buf[i] == '\n') { 82 | buf[i] = '\0'; 83 | 84 | char p1[PATH_MAX]; 85 | char p2[PATH_MAX]; 86 | char p3[PATH_MAX]; 87 | char source[PATH_MAX]; 88 | char target[PATH_MAX]; 89 | char p6[PATH_MAX]; 90 | char p7[PATH_MAX]; 91 | char p8[PATH_MAX]; 92 | char fs[PATH_MAX]; 93 | 94 | if (sscanf(start, "%s %s %s %s %s %s %s %s %s", p1, p2, p3, source, target, p6, p7, p8, fs) == 9) { 95 | if ( 96 | (strcmp(target, "/sbin") == 0) || 97 | (strncmp(target, "/sbin/", 6) == 0) || 98 | (strcmp(target, "/root/sbin") == 0) || 99 | (strncmp(target, "/root/sbin/", 11) == 0) || 100 | (strcmp(target, "/data/adb/su") == 0) || //TODO readlink /sbin/supersu ? 101 | (strncmp(target, "/data/adb/su/", 13) == 0) || 102 | (strstr(source, "/adb/su") != NULL) || 103 | (strstr(target, "/system/") != NULL) || 104 | (strstr(target, "/vendor/") != NULL) || 105 | (strstr(target, "/original/") != NULL) || 106 | ( 107 | ( 108 | (strcmp(fs, "tmpfs") == 0) 109 | ) && ( 110 | (strcmp(target, "/system") == 0) || 111 | (strcmp(target, "/vendor") == 0) || 112 | (strcmp(target, "/oem") == 0) || 113 | (strcmp(target, "/odm") == 0) 114 | ) 115 | ) 116 | ) { 117 | if (umount2(target, MNT_DETACH) == 0) { 118 | LOGD("[%d] [%s] unmounted", pid, target); 119 | } else { 120 | LOGD("[%d] [%s] unmount failed", pid, target); 121 | } 122 | } 123 | } 124 | 125 | start = &buf[i + 1]; 126 | } 127 | } 128 | } else { 129 | LOGD("[%d] empty read from mountinfo", pid); 130 | } 131 | } else { 132 | LOGD("[%d] failed to read mountinfo", pid); 133 | } 134 | } else { 135 | LOGD("[%d] failed to join namespace", pid); 136 | } 137 | close(nsfd); 138 | } else { 139 | LOGD("[%d] failed to join namespace", pid); 140 | } 141 | } 142 | } 143 | exit(EXIT_SUCCESS); 144 | } else { 145 | // parent 146 | waitpid(child, NULL, 0); 147 | } 148 | } 149 | 150 | // detects if a pid (that has been forked/cloned from zygote) has changed its name to its 151 | // final form (usually based on package name), check if that package is supposed to have root, 152 | // and if not, unmount root-related mounts from its namespace. 153 | static int detect_package_and_unmount(int pid, int zygote) { 154 | char path[PATH_MAX]; 155 | struct stat stat; 156 | snprintf(path, PATH_MAX, "/proc/%d/task", pid); 157 | lstat(path, &stat); 158 | if (stat.st_uid == 0) return 0; 159 | 160 | char cmdline[128]; 161 | snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid); 162 | int fd = open(path, O_RDONLY); 163 | if (fd >= 0) { 164 | int len = read(fd, cmdline, 128); 165 | if ((len > 0) && (len < 128)) { 166 | cmdline[len] = '\0'; 167 | for (int i = 0; i < len; i++) { 168 | if ((cmdline[i] == ' ') || (cmdline[i] == ':') || (cmdline[i] == '\0')) { 169 | cmdline[i] = '\0'; 170 | break; 171 | } 172 | } 173 | } 174 | close(fd); 175 | 176 | if ((strcmp(cmdline, "zygote") != 0) && (strcmp(cmdline, "zygote64") != 0) && (strncmp(cmdline, "<", 1) != 0)) { 177 | // Name has been prettified at this point, (see com_android_internal_os_Zygote.cpp::setThreadName() or 178 | // ZygoteConnection.java::handleChildProc()). 179 | // The process's mount namespace should already be private (see com_android_internal_os_Zygote.cpp::MountEmulatedStorage()). 180 | // Just after those two things happen, zygote is still single-threaded, but an Android 181 | // app never is. This code here is executed when the second thread is created. 182 | 183 | LOGD("[%d] forked [%s] (%d)", pid, cmdline, stat.st_uid); 184 | 185 | load_config(); 186 | if (!allow_root_for_uid(stat.st_uid) || !allow_root_for_name(cmdline)) { 187 | unmount_root(cmdline, zygote, pid); 188 | } 189 | return 1; 190 | } 191 | } 192 | return 0; 193 | } 194 | 195 | int main(int argc, char *argv[], char** envp) { 196 | (void)detach_tid; // prevent unused function error 197 | 198 | if (argc != 2) { 199 | LOGD("Usage: %s ", LOG_TAG); 200 | return 1; 201 | } 202 | pid_t target = atol(argv[1]); 203 | if (target == 0) { 204 | LOGD("Invalid pid passed [%s]", argv[1]); 205 | return 1; 206 | } 207 | 208 | #ifndef DEBUG 209 | // make ourselves less obvious in ps output 210 | prettify(argc, argv, strstr(LOG_TAG, "64") == 0 ? "zygote64" : "zygote"); 211 | #endif 212 | 213 | // attach to target and monitor its forks and clones 214 | if (trace(PTRACE_ATTACH, target, NULL, 0) != -1) { 215 | LOGD("Attached to [%d]", target); 216 | wait_stop(target); 217 | 218 | trace(PTRACE_SETOPTIONS, target, NULL, 219 | PTRACE_O_TRACECLONE | 220 | // PTRACE_O_TRACEEXEC | #do not want 221 | PTRACE_O_TRACEEXIT | 222 | PTRACE_O_TRACEFORK | 223 | // PTRACE_O_TRACESYSGOOD | #do not want 224 | PTRACE_O_TRACEVFORK // | 225 | // PTRACE_O_TRACEVFORKDONE | #do not want 226 | // PTRACE_O_TRACESECCOMP | #do not want 227 | // PTRACE_O_SUSPEND_SECCOMP #do not want and does not exist in headers 228 | ); 229 | 230 | #define PID_MAX 32768 231 | int first_stop[PID_MAX] = {0}; 232 | int forked[PID_MAX] = {0}; 233 | int parent[PID_MAX] = {0}; 234 | 235 | int status; 236 | trace(PTRACE_CONT, target, NULL, 0); 237 | while (1) { 238 | int detached = 0; 239 | int pid = waitpid(-1, &status, __WALL); 240 | int signal = 0; 241 | if (pid > 0) { 242 | LOGD("[%d] waitpid", pid); 243 | if (WIFSTOPPED(status)) { 244 | LOGD("[%d] stopped", pid); 245 | if (WSTOPSIG(status) == SIGTRAP) { 246 | if (WEVENT(status) != 0) { // not sure yet why those happen 247 | // see https://lwn.net/Articles/446593/ for some of this handling 248 | #ifdef DEBUG 249 | char* event = "?"; 250 | switch (WEVENT(status)) { 251 | case PTRACE_EVENT_FORK: event = "FORK"; break; 252 | case PTRACE_EVENT_VFORK: event = "VFORK"; break; 253 | case PTRACE_EVENT_CLONE: event = "CLONE"; break; 254 | case PTRACE_EVENT_EXIT: event = "EXIT"; break; 255 | } 256 | #endif 257 | int childpid = -1; 258 | trace(PTRACE_GETEVENTMSG, pid, 0, (size_t)&childpid); 259 | LOGD("[%d] trapped: [%s][%d] [%d]", pid, event, WEVENT(status), childpid); 260 | 261 | if ((WEVENT(status) == PTRACE_EVENT_FORK) || (WEVENT(status) == PTRACE_EVENT_VFORK) || (WEVENT(status) == PTRACE_EVENT_CLONE)) { 262 | if ((pid == target) && (WEVENT(status) != PTRACE_EVENT_CLONE)) { // fork of target 263 | forked[childpid] = 1; 264 | parent[childpid] = childpid; 265 | } else if (forked[pid] && (WEVENT(status) == PTRACE_EVENT_CLONE)) { // clone of fork 266 | forked[childpid] = 1; 267 | int p = pid; 268 | while ((p != 0) && (parent[p] != p)) p = parent[p]; 269 | parent[childpid] = p; 270 | 271 | if (detect_package_and_unmount(p, target)) { 272 | LOGD("[%d] package detected [%d]", pid, childpid); 273 | signal = -1; 274 | if (trace(PTRACE_CONT, pid, NULL, 0) != ESRCH) { 275 | detach_pid(p); 276 | } 277 | } else { 278 | LOGD("[%d] package NOT detected [%d]", pid, childpid); 279 | } 280 | } else { 281 | forked[childpid] = 0; 282 | parent[childpid] = 0; 283 | } 284 | first_stop[childpid] = 1; 285 | } else if (WEVENT(status) == PTRACE_EVENT_EXIT) { 286 | // use pid here, not childpid ! 287 | if (pid == target) 288 | break; 289 | trace(PTRACE_CONT, pid, NULL, 0); 290 | detached = 1; 291 | } 292 | } 293 | } else { 294 | if (first_stop[pid]) { 295 | // new fork or clone starts with a STOP signal 296 | 297 | first_stop[pid] = 0; 298 | if (forked[pid]) { 299 | // we don't want forks of our forks to be traced, but we do want clones (threads) 300 | trace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); 301 | } 302 | 303 | LOGD("[%d] stopped (first): %d [%08x]", pid, WSTOPSIG(status), status); 304 | } else if (WSTOPSIG(status) != SIGSTOP) { // we cause SIGSTOP, ignore and drop 305 | pid_t from = -1; 306 | (void)from; // unused variable error 307 | siginfo_t siginfo; 308 | if (ptrace(PTRACE_GETSIGINFO, pid, 0, (size_t)&siginfo) == 0) { 309 | from = siginfo.si_pid; 310 | } 311 | 312 | LOGD("[%d] stopped: %d from [%d]", pid, WSTOPSIG(status), from); 313 | signal = WSTOPSIG(status); 314 | } 315 | } 316 | } else if (WIFSIGNALED(status)) { 317 | LOGD("[%d] signaled: %d", pid, WTERMSIG(status)); 318 | if (pid == target) 319 | break; 320 | detached = 1; 321 | } else if (status == 0) { 322 | LOGD("[%d] died: %d", pid, status); 323 | if (pid == target) 324 | break; 325 | detached = 1; 326 | } else { 327 | LOGD("[%d] status: %d", pid, status); 328 | } 329 | if (!detached) { 330 | if (signal >= 0) { 331 | trace(PTRACE_CONT, pid, NULL, signal); 332 | } 333 | } else { 334 | first_stop[pid] = 0; 335 | forked[pid] = 0; 336 | parent[pid] = 0; 337 | } 338 | } 339 | } 340 | 341 | // detach 342 | trace(PTRACE_DETACH, target, NULL, 0); 343 | kill(target, SIGCONT); 344 | LOGD("Detached from [%d]", target); 345 | } else { 346 | LOGD("Attach failed [%d]", errno); 347 | return 1; 348 | } 349 | 350 | return 0; 351 | } -------------------------------------------------------------------------------- /suhide/native/suhide_launcher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* Main file for suhide (launcher), which launches suhide[32|64] children that 18 | * attach to zygote(64) as debugger to hide root. Additionally, it monitors 19 | * hardware button input and (un)hides packages. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "ndklog.h" 30 | #include "util.h" 31 | #include "getevent.h" 32 | 33 | // pids for currently running versions of suhide and zygote 34 | pid_t suhide32 = 0; 35 | pid_t suhide64 = 0; 36 | pid_t zygote32 = 0; 37 | pid_t zygote64 = 0; 38 | 39 | // do we have 64-bit versions? 40 | int have64 = 0; 41 | 42 | // get path to executable, self must be PATH_MAX in size, returns 0 on success 43 | static int get_self(char* self) { 44 | int len = readlink("/proc/self/exe", self, PATH_MAX); 45 | if ((len == 0) || (len >= PATH_MAX)) return 1; 46 | self[len] = '\0'; 47 | return 0; 48 | } 49 | 50 | // get path to suhide executable, self and suhide must be PATH_MAX in size, returns 0 on success 51 | static int get_suhide(char* bits, char* self, char* suhide) { 52 | memcpy(suhide, self, strlen(self)); 53 | memcpy(&suhide[strlen(self)], bits, strlen(bits) + 1); 54 | if (access(suhide, X_OK) != 0) { 55 | suhide[0] = '\0'; 56 | return 1; 57 | } 58 | return 0; 59 | } 60 | 61 | // find pid for process, returns 0 on error 62 | static pid_t find_process(char* name) { 63 | char buf[PATH_MAX], path[PATH_MAX]; 64 | pid_t ret = 0; 65 | DIR* dir; 66 | struct dirent *ent; 67 | if ((dir = opendir("/proc/")) != NULL) { 68 | while ((ent = readdir(dir)) != NULL) { 69 | pid_t pid = atoi(ent->d_name); 70 | if (pid > 0) { 71 | memset(path, 0, 64); 72 | snprintf(path, 64, "/proc/%d/exe", pid); 73 | int len = readlink(path, buf, PATH_MAX); 74 | if ((len >= 0) && (len < PATH_MAX)) { 75 | buf[len] = '\0'; 76 | if (strstr(buf, "app_process") != NULL) { 77 | memset(path, 0, 64); 78 | snprintf(path, 64, "/proc/%d/cmdline", pid); 79 | int fd = open(path, O_RDONLY); 80 | if (fd >= 0) { 81 | if (read(fd, buf, PATH_MAX) > strlen(name)) { 82 | if ((strncmp(buf, name, strlen(name)) == 0) && ((buf[strlen(name)] == '\0') || (buf[strlen(name)] == ' '))) { 83 | ret = pid; 84 | } 85 | } 86 | close(fd); 87 | } 88 | } 89 | } 90 | } 91 | if (ret > 0) break; 92 | } 93 | closedir(dir); 94 | } 95 | return ret; 96 | } 97 | 98 | // launch child suhide process with zygote parameter, returns new pid 99 | static pid_t launch_child(char* path, pid_t zygote) { 100 | if (strlen(path) == 0) return 0; 101 | if (zygote == 0) return 0; 102 | 103 | #ifdef DEBUG 104 | fprintf(stderr, "launching: %s\n", path); 105 | #endif 106 | 107 | pid_t child = fork(); 108 | if (child == 0) { 109 | char param[PATH_MAX]; 110 | snprintf(param, PATH_MAX, "%d", zygote); 111 | execl(path, path, param, (char*)NULL); 112 | exit(EXIT_FAILURE); 113 | } 114 | 115 | #ifdef DEBUG 116 | fprintf(stderr, "-- pid: %d\n", child); 117 | #endif 118 | 119 | return child; 120 | } 121 | 122 | static void stop_android() { 123 | // just for testing purposes, requires calling 'start' twice, doesn't work right with 32+64 bit systems 124 | /* 125 | pid_t child = fork(); 126 | if (child == 0) { 127 | execl("/system/bin/stop", "stop", NULL); 128 | exit(EXIT_FAILURE); 129 | } 130 | int status; 131 | waitpid(child, &status, 0); 132 | */ 133 | } 134 | 135 | // run the switch_packages script to hide/unhide selected packages 136 | static void switch_packages() { 137 | pid_t child = fork(); 138 | if (child == 0) { 139 | execl("/sbin/supersu/suhide/switch_packages", "switch_packages", (char*)NULL); 140 | exit(EXIT_FAILURE); 141 | } 142 | } 143 | 144 | int main(int argc, char *argv[], char** envp) { 145 | // start with --nodaemon for debugging purposes 146 | if (!((argc >= 2) && (strcmp(argv[1], "--nodaemon") == 0))) { 147 | fork_daemon(0); 148 | } 149 | 150 | #ifndef DEBUG 151 | // make ourselves less obvious in ps output 152 | prettify(argc, argv, "system_server"); 153 | #endif 154 | 155 | // find paths to 32 and 64-bit version of suhide 156 | char path_self[PATH_MAX], path_suhide32[PATH_MAX], path_suhide64[PATH_MAX]; 157 | if (get_self(path_self) != 0) return 1; 158 | get_suhide("32", path_self, path_suhide32); 159 | have64 = get_suhide("64", path_self, path_suhide64) == 0 ? 1 : 0; 160 | 161 | // last 6 input_events 162 | struct input_event events[6]; 163 | memset(&events[0], 0, sizeof(events[0]) * 6); 164 | 165 | // never quit 166 | while (1) { 167 | #ifdef DEBUG 168 | fprintf(stderr, "outer loop\n"); 169 | #endif 170 | 171 | // find 32 and 64-bit zygote processes, waiting for them to start if not yet running 172 | while ((zygote32 == 0) || ((zygote64 == 0) && (have64 == 1))) { 173 | int found = 0; 174 | if (zygote32 == 0) { 175 | zygote32 = find_process("zygote"); 176 | if (zygote32 != 0) { 177 | fprintf(stderr, "zygote32: %d\n", zygote32); 178 | found = 1; 179 | } 180 | } else if ((zygote64 == 0) && (have64 == 1)) { 181 | zygote64 = find_process("zygote64"); 182 | if (zygote64 != 0) { 183 | fprintf(stderr, "zygote64: %d\n", zygote64); 184 | found = 1; 185 | } 186 | } 187 | 188 | if (!found) 189 | ms_sleep(128); 190 | } 191 | 192 | // launch suhide processes if not yet running 193 | if (suhide32 == 0) suhide32 = launch_child(path_suhide32, zygote32); 194 | if (suhide64 == 0) suhide64 = launch_child(path_suhide64, zygote64); 195 | 196 | // (re-)initialize input event monitoring 197 | reset_getevent(); 198 | 199 | // loop until one of our suhide children exits 200 | while (1) { 201 | #ifdef DEBUG 202 | fprintf(stderr, "inner loop\n"); 203 | #endif 204 | 205 | // input detection loop, waits one second for input and keeps looping until 206 | // there hasn't been any input for again one second 207 | struct input_event event; 208 | int event_count = 0; 209 | while ((event_count = get_event(&event, 1000)) > 0) { 210 | #ifdef DEBUG 211 | fprintf(stderr, "event loop\n"); 212 | #endif 213 | 214 | if ( 215 | (event.type == EV_KEY) && 216 | (event.value == 1) // down 217 | ) { 218 | int i; 219 | for (i = 0; i < 5; i++) { 220 | events[i] = events[i + 1]; 221 | } 222 | events[5] = event; 223 | 224 | // UP, DOWN, UP, DOWN, UP DOWN, 3 seconds, no other keys 225 | if ( 226 | (events[0].code == KEY_VOLUMEUP) && 227 | (events[1].code == KEY_VOLUMEDOWN) && 228 | (events[2].code == KEY_VOLUMEUP) && 229 | (events[3].code == KEY_VOLUMEDOWN) && 230 | (events[4].code == KEY_VOLUMEUP) && 231 | (events[5].code == KEY_VOLUMEDOWN) 232 | ) { 233 | struct timeval diff; 234 | timersub(&events[3].time, &events[0].time, &diff); 235 | if ((int)diff.tv_sec < 3) { 236 | memset(&events[0], 0, sizeof(events[0]) * 6); 237 | fprintf(stderr, "Switching package visibility...\n"); 238 | switch_packages(); 239 | } 240 | } 241 | } 242 | } 243 | 244 | // error occurred, probably a device got added or removed, re-init 245 | if (event_count < 0) break; 246 | 247 | #ifdef DEBUG 248 | fprintf(stderr, "waitpid\n"); 249 | #endif 250 | 251 | // check if our suhide children are still alive, break parent loop and re-init otherwise 252 | int status; 253 | pid_t waited = waitpid(0, &status, WNOHANG); 254 | if ((waited > 0) && ((waited == suhide32) || (waited == suhide64))) { 255 | if (WIFEXITED(status) || WIFSIGNALED(status)) { 256 | if (waited == suhide32) { 257 | stop_android(); 258 | fprintf(stderr, "suhide32 stopped, restarting\n"); 259 | suhide32 = 0; 260 | zygote32 = 0; 261 | break; 262 | } else if (waited == suhide64) { 263 | stop_android(); 264 | fprintf(stderr, "suhide64 stopped, restarting\n"); 265 | suhide64 = 0; 266 | zygote64 = 0; 267 | break; 268 | } 269 | } 270 | } 271 | } 272 | } 273 | 274 | return 0; 275 | } -------------------------------------------------------------------------------- /suhide/native/trace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ndklog.h" 26 | #include "util.h" 27 | 28 | // missing declaration 29 | int tgkill(int tgid, int tid, int sig); 30 | 31 | // ptrace with a log wrapper 32 | long trace(int request, pid_t pid, void *addr, size_t data) { 33 | long ret = ptrace(request, pid, (caddr_t) addr, (void *) data); 34 | if (ret == -1) { 35 | LOGD("TRACE(%d, %d, %d, %d): %d", request, pid, (int)(uintptr_t)addr, (int)data, errno); 36 | } 37 | return ret; 38 | } 39 | 40 | // send SIGCONT via tgkill 41 | int cont(pid_t group, pid_t target) { 42 | return tgkill(group, target, SIGCONT); 43 | } 44 | 45 | // send SIGSTOP via tgkill 46 | int stop(pid_t group, pid_t target) { 47 | return tgkill(group, target, SIGSTOP); 48 | } 49 | 50 | // wait for target to stop 51 | void wait_stop(pid_t target) { 52 | LOGD("[%d] wait_stop(%d)", target, target); 53 | struct timeval start = timestamp(); 54 | while (1) { 55 | int status; 56 | int pid = waitpid(target, &status, __WALL | WNOHANG); 57 | LOGD("[%d] waitpid --> %d/%d", target, pid, status); 58 | if ((pid == target) && WIFSTOPPED(status)) { 59 | break; 60 | } else if (pid == -1) { 61 | // error 62 | LOGD("[%d] waitpid --> error", target); 63 | break; 64 | } else { 65 | if (timestamp_diff_ms(timestamp(), start) > 128) { 66 | LOGD("[%d] waitpid --> timeout, zombie?", target); 67 | break; 68 | } 69 | ms_sleep(1); 70 | } 71 | } 72 | LOGD("[%d] /wait_stop(%d)", target, target); 73 | } 74 | 75 | // stop target and wait for it to become stopped 76 | int stop_and_wait_stop(pid_t group, pid_t target) { 77 | LOGD("[%d] stop_and_wait_stop(%d, %d)", group, group, target); 78 | if (stop(group, target) == 0) { 79 | wait_stop(target); 80 | return 0; 81 | } 82 | LOGD("[%d] /stop_and_wait_stop(%d, %d) ABORT", group, group, target); 83 | return 1; 84 | } 85 | 86 | // detach from thread and continue it 87 | static void detach_tid(int pid, int tid) { 88 | if (tgkill(pid, tid, SIGSTOP) == 0) { 89 | wait_stop(tid); 90 | trace(PTRACE_SETOPTIONS, tid, NULL, 0); 91 | trace(PTRACE_DETACH, tid, NULL, 0); 92 | tgkill(pid, tid, SIGCONT); 93 | } 94 | } 95 | 96 | // stop target (thread) and detach from it 97 | int stop_and_detach(pid_t group, pid_t target) { 98 | LOGD("[%d] stop_and_detach(%d, %d)", group, group, target); 99 | if (ptrace(PTRACE_SETOPTIONS, target, NULL, 0) == 0) { 100 | trace(PTRACE_DETACH, target, NULL, 0); 101 | LOGD("[%d] /stop_and_detach(%d, %d) DIRECT", group, group, target); 102 | return 0; 103 | } else if (stop_and_wait_stop(group, target) == 0) { 104 | trace(PTRACE_SETOPTIONS, target, NULL, 0); 105 | trace(PTRACE_DETACH, target, NULL, 0); 106 | LOGD("[%d] /stop_and_detach(%d, %d) STOP/WAIT", group, group, target); 107 | return 0; 108 | } 109 | LOGD("[%d] /stop_and_detach(%d, %d) OTHER", group, group, target); 110 | return 1; 111 | } 112 | 113 | // stop and detach from each of pid's threads, then continue pid's execution 114 | void detach_pid(int pid) { 115 | char task[PATH_MAX]; 116 | snprintf(task, PATH_MAX, "/proc/%d/task", pid); 117 | 118 | LOGD("[%d] detaching", pid); 119 | stop_and_detach(pid, pid); 120 | 121 | DIR* dir; 122 | struct dirent *ent; 123 | if ((dir = opendir(task)) != NULL) { 124 | while ((ent = readdir(dir)) != NULL) { 125 | pid_t tid = atoi(ent->d_name); 126 | if ((tid > 0) && (tid != pid)) { 127 | stop_and_detach(pid, tid); 128 | } 129 | } 130 | closedir(dir); 131 | } 132 | 133 | cont(pid, pid); 134 | cont(pid, pid); // yes, twice 135 | 136 | LOGD("[%d] detached", pid); 137 | } 138 | -------------------------------------------------------------------------------- /suhide/native/trace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _TRACE_H 18 | #define _TRACE_H 19 | 20 | #define SIGSTOPSYSTRACE 0x80 //see PTRACE_O_TRACESYSGOOD 21 | 22 | #define WEVENT(s) (((s) & 0xffff0000) >> 16) 23 | 24 | long trace(int request, pid_t pid, void *addr, size_t data); 25 | int cont(pid_t group, pid_t target); 26 | int stop(pid_t group, pid_t target); 27 | void wait_stop(pid_t target); 28 | int stop_and_wait_stop(pid_t group, pid_t target); 29 | int stop_and_detach(pid_t group, pid_t target); 30 | void detach_pid(int pid); 31 | void detach_tid(int pid, int tid); 32 | 33 | #endif -------------------------------------------------------------------------------- /suhide/native/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ndklog.h" 26 | 27 | #ifndef DEBUG 28 | // prettify process name for ps output 29 | // we should really be using prctl(PR_SET_NAME, ...) or pthread_getname_np for this... 30 | void prettify(int argc, char* argv[], char* pretty) { 31 | for (int i = 0; i < argc; i++) { 32 | memset(argv[i], ' ', strlen(argv[i])); 33 | } 34 | memcpy(argv[0], pretty, strlen(pretty) + 1); 35 | } 36 | #endif 37 | 38 | // close all fds we don't need 39 | void close_parent_fds(int* except, int len) { 40 | DIR* dir; 41 | struct dirent *ent; 42 | if ((dir = opendir("/proc/self/fd")) != NULL) { 43 | while ((ent = readdir(dir)) != NULL) { 44 | int fd = atoi(ent->d_name); 45 | if (fd > 2) { 46 | int doclose = 1; 47 | 48 | int i; 49 | for (i = 0; i < len; i++) { 50 | if (except[i] == fd) { 51 | doclose = 0; 52 | break; 53 | } 54 | } 55 | 56 | if (doclose) { 57 | char path[PATH_MAX]; 58 | char link[PATH_MAX]; 59 | memset(path, 0, PATH_MAX); 60 | memset(link, 0, PATH_MAX); 61 | snprintf(path, sizeof(path), "/proc/self/fd/%s", ent->d_name); 62 | 63 | int linklen = readlink(path, link, PATH_MAX); 64 | if (linklen > 0) { 65 | if (strncmp("/dev/__properties__", link, 19) == 0) { 66 | // keep properties readable 67 | doclose = 0; 68 | } 69 | } 70 | } 71 | 72 | if (doclose) { 73 | close(fd); 74 | } 75 | } 76 | } 77 | closedir(dir); 78 | } 79 | 80 | return; 81 | } 82 | 83 | // become a daemon by forking twice with a setsid in between; exits current process unless 84 | // returnParent is set; returns -1 for error. Note that a second fork error is not detected, 85 | // though these are extremely rare. 86 | int fork_daemon(int returnParent) { 87 | pid_t child = fork(); 88 | if (child == 0) { 89 | close(STDIN_FILENO); 90 | close(STDOUT_FILENO); 91 | close(STDERR_FILENO); 92 | 93 | int devNull = open("/dev/null", O_RDWR); 94 | dup2(devNull, STDIN_FILENO); 95 | dup2(devNull, STDOUT_FILENO); 96 | dup2(devNull, STDERR_FILENO); 97 | close(devNull); 98 | 99 | close_parent_fds(NULL, 0); 100 | 101 | setsid(); 102 | pid_t child2 = fork(); 103 | if (child2 <= 0) return 0; // success child or error on 2nd fork 104 | exit(EXIT_SUCCESS); 105 | } 106 | if (child < 0) return -1; // error on 1st fork 107 | int status; 108 | waitpid(child, &status, 0); 109 | if (!returnParent) exit(EXIT_SUCCESS); 110 | return 1; // success parent 111 | } 112 | 113 | // sleep for ms ms, returns ms left if EINTR occurs 114 | int ms_sleep(int ms) { 115 | struct timespec ts; 116 | ts.tv_sec = ms / 1000; 117 | ts.tv_nsec = (ms % 1000) * 1000000; 118 | if ((nanosleep(&ts,&ts) == -1) && (errno == EINTR)) { 119 | int ret = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); 120 | if (ret < 1) ret = 1; 121 | return ret; 122 | } 123 | return 0; 124 | } 125 | 126 | // get current timestamp 127 | struct timeval timestamp() { 128 | struct timeval tv; 129 | gettimeofday(&tv,NULL); 130 | return tv; 131 | } 132 | 133 | // get the difference in ms between two timestamps 134 | int timestamp_diff_ms(struct timeval x, struct timeval y) { 135 | if (x.tv_usec < y.tv_usec) { 136 | int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; 137 | y.tv_usec -= 1000000 * nsec; 138 | y.tv_sec += nsec; 139 | } 140 | if (x.tv_usec - y.tv_usec > 1000000) { 141 | int nsec = (x.tv_usec - y.tv_usec) / 1000000; 142 | y.tv_usec += 1000000 * nsec; 143 | y.tv_sec -= nsec; 144 | } 145 | return ((x.tv_sec - y.tv_sec) * 1000) + ((x.tv_usec - y.tv_usec) / 1000); 146 | } 147 | -------------------------------------------------------------------------------- /suhide/native/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef _UTIL_H 18 | #define _UTIL_H 19 | 20 | #ifndef DEBUG 21 | void prettify(int argc, char* argv[], char* pretty); 22 | #endif 23 | 24 | int fork_daemon(int returnParent); 25 | 26 | int ms_sleep(int ms); 27 | 28 | struct timeval timestamp(); 29 | int timestamp_diff_ms(struct timeval x, struct timeval y); 30 | 31 | #endif -------------------------------------------------------------------------------- /suhide/proguard-project.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/proguard-project.txt -------------------------------------------------------------------------------- /suhide/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\SoftDev\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /suhide/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /suhide/src/main/java/eu/chainfire/suhide/AppAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.chainfire.suhide; 18 | 19 | import android.content.Context; 20 | import android.content.pm.ApplicationInfo; 21 | import android.content.pm.PackageManager; 22 | import android.graphics.drawable.Drawable; 23 | import android.support.v7.widget.RecyclerView; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.ImageView; 28 | import android.widget.TextView; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Collections; 32 | import java.util.Comparator; 33 | import java.util.List; 34 | 35 | import eu.chainfire.suhide.AppFragment.OnListFragmentInteractionListener; 36 | 37 | public class AppAdapter extends RecyclerView.Adapter { 38 | private final List items; 39 | private OnListFragmentInteractionListener listener; 40 | 41 | public AppAdapter() { 42 | this.items = new ArrayList(); 43 | this.listener = null; 44 | } 45 | 46 | public void setOnListFragmentInteractionListener(OnListFragmentInteractionListener listener) { 47 | this.listener = listener; 48 | } 49 | 50 | @Override 51 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 52 | View view = LayoutInflater.from(parent.getContext()) 53 | .inflate(R.layout.fragment_app, parent, false); 54 | return new ViewHolder(view); 55 | } 56 | 57 | @Override 58 | public void onBindViewHolder(final ViewHolder holder, int position) { 59 | holder.appItem = items.get(position); 60 | holder.ivIcon.setImageDrawable(holder.appItem.icon); 61 | if (holder.appItem.title == null) { 62 | holder.tvTitle.setText(holder.appItem.packageName); 63 | holder.tvPackageName.setText(String.valueOf(holder.appItem.uid)); 64 | } else { 65 | holder.tvTitle.setText(holder.appItem.title); 66 | holder.tvPackageName.setText(String.valueOf(holder.appItem.uid) + " " + holder.appItem.packageName); 67 | } 68 | switch (holder.appItem.state) { 69 | case AppItem.ROOT: holder.ivState.setImageResource(R.drawable.ic_root); break; 70 | case AppItem.NO_ROOT: holder.ivState.setImageResource(R.drawable.ic_no_root); break; 71 | case AppItem.HIDDEN: holder.ivState.setImageResource(R.drawable.ic_hidden); break; 72 | } 73 | holder.view.setOnClickListener(new View.OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | if (null != listener) { 77 | listener.onListFragmentInteraction(holder.appItem); 78 | } 79 | } 80 | }); 81 | } 82 | 83 | @Override 84 | public int getItemCount() { 85 | return items.size(); 86 | } 87 | 88 | public AppItem getItem(int position) { 89 | return items.get(position); 90 | } 91 | 92 | public class ViewHolder extends RecyclerView.ViewHolder { 93 | public final View view; 94 | public final ImageView ivIcon; 95 | public final TextView tvTitle; 96 | public final TextView tvPackageName; 97 | public final ImageView ivState; 98 | public AppItem appItem; 99 | 100 | public ViewHolder(View view) { 101 | super(view); 102 | this.view = view; 103 | ivIcon = (ImageView) view.findViewById(R.id.icon); 104 | tvTitle = (TextView) view.findViewById(R.id.title); 105 | tvPackageName = (TextView) view.findViewById(R.id.packageName); 106 | ivState = (ImageView) view.findViewById(R.id.state); 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | return super.toString() + " '" + appItem.packageName + "'"; 112 | } 113 | } 114 | 115 | private static String getLabel(PackageManager pm, ApplicationInfo info) { 116 | try { 117 | return info.loadLabel(pm).toString(); 118 | } catch (Exception e) { 119 | return null; 120 | } 121 | } 122 | 123 | private static Drawable getIcon(PackageManager pm, ApplicationInfo info) { 124 | try { 125 | return info.loadIcon(pm); 126 | } catch (Exception e) { 127 | return null; 128 | } 129 | } 130 | 131 | public void loadAppItems(Context context) { 132 | List results = new ArrayList(); 133 | PackageManager pm = context.getPackageManager(); 134 | List applications = pm.getInstalledApplications(PackageManager.GET_META_DATA); 135 | for (ApplicationInfo info : applications) { 136 | if (info.uid >= 10000) { 137 | results.add(new AppItem( 138 | getLabel(pm, info), 139 | info.uid, 140 | info.packageName, 141 | getIcon(pm, info), 142 | AppItem.ROOT 143 | )); 144 | } 145 | } 146 | Collections.sort(results, new Comparator() { 147 | @Override 148 | public int compare(AppItem a, AppItem b) { 149 | if ((a.title == null) && (b.title == null)) { 150 | return a.packageName.compareToIgnoreCase(b.packageName); 151 | } else if ((a.title == null) && (b.title != null)) { 152 | return 1; 153 | } else if ((a.title != null) && (b.title == null)) { 154 | return -1; 155 | } else { 156 | return a.title.compareToIgnoreCase(b.title); 157 | } 158 | } 159 | }); 160 | items.clear(); 161 | for (AppItem item : results) { 162 | items.add(item); 163 | } 164 | } 165 | 166 | public class AppItem { 167 | public static final int ROOT = 0; 168 | public static final int NO_ROOT = 1; 169 | public static final int HIDDEN = 2; 170 | 171 | public final String title; 172 | public final int uid; 173 | public final String packageName; 174 | public final Drawable icon; 175 | public int state; 176 | 177 | public AppItem(String title, int uid, String packageName, Drawable icon, int state) { 178 | if ((title != null) && (packageName != null) && (title.equals(packageName))) { 179 | title = null; 180 | } 181 | this.title = title; 182 | this.uid = uid; 183 | this.packageName = packageName; 184 | this.icon = icon; 185 | this.state = state; 186 | } 187 | 188 | private void changed() { 189 | int index = -1; 190 | for (int i = 0; i < items.size(); i++) { 191 | if (items.get(i) == this) { 192 | index = i; 193 | break; 194 | } 195 | } 196 | if (index == -1) { 197 | // this kills the ripple 198 | notifyDataSetChanged(); 199 | } else { 200 | notifyItemChanged(index); 201 | } 202 | } 203 | 204 | public int nextState(boolean uid) { 205 | if (state < HIDDEN) { 206 | state++; 207 | } else { 208 | state = ROOT; 209 | } 210 | changed(); 211 | if (uid) { 212 | for (int i = 0; i < items.size(); i++) { 213 | if (items.get(i).uid == this.uid) { 214 | items.get(i).setState(state); 215 | } 216 | } 217 | } 218 | return state; 219 | } 220 | 221 | public void setState(int state) { 222 | this.state = state; 223 | changed(); 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /suhide/src/main/java/eu/chainfire/suhide/AppFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.chainfire.suhide; 18 | 19 | import android.content.Context; 20 | import android.os.Bundle; 21 | import android.support.v4.app.Fragment; 22 | import android.support.v7.widget.DividerItemDecoration; 23 | import android.support.v7.widget.LinearLayoutManager; 24 | import android.support.v7.widget.RecyclerView; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import eu.chainfire.suhide.AppAdapter.AppItem; 30 | 31 | public class AppFragment extends Fragment { 32 | private OnListFragmentInteractionListener listener; 33 | private AppAdapter adapter = null; 34 | 35 | public AppFragment() { 36 | } 37 | 38 | public static AppFragment newInstance() { 39 | AppFragment fragment = new AppFragment(); 40 | Bundle args = new Bundle(); 41 | fragment.setArguments(args); 42 | return fragment; 43 | } 44 | 45 | @Override 46 | public void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | } 49 | 50 | @Override 51 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 52 | Bundle savedInstanceState) { 53 | View view = inflater.inflate(R.layout.fragment_app_list, container, false); 54 | if (view instanceof RecyclerView) { 55 | Context context = view.getContext(); 56 | RecyclerView recyclerView = (RecyclerView) view; 57 | recyclerView.setLayoutManager(new LinearLayoutManager(context)); 58 | recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), LinearLayoutManager.VERTICAL)); 59 | adapter = ((MyApplication)getActivity().getApplication()).getAppAdapter(); 60 | adapter.setOnListFragmentInteractionListener(new OnListFragmentInteractionListener() { 61 | @Override 62 | public void onListFragmentInteraction(AppItem item) { 63 | if (listener != null) { 64 | listener.onListFragmentInteraction(item); 65 | } 66 | } 67 | }); 68 | recyclerView.setAdapter(adapter); 69 | } 70 | return view; 71 | } 72 | 73 | @Override 74 | public void onAttach(Context context) { 75 | super.onAttach(context); 76 | if (context instanceof OnListFragmentInteractionListener) { 77 | listener = (OnListFragmentInteractionListener) context; 78 | } 79 | } 80 | 81 | @Override 82 | public void onDetach() { 83 | super.onDetach(); 84 | listener = null; 85 | } 86 | 87 | public interface OnListFragmentInteractionListener { 88 | void onListFragmentInteraction(AppItem item); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /suhide/src/main/java/eu/chainfire/suhide/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.chainfire.suhide; 18 | 19 | import android.app.ProgressDialog; 20 | import android.os.AsyncTask; 21 | import android.os.Bundle; 22 | import android.support.design.widget.Snackbar; 23 | import android.support.design.widget.TabLayout; 24 | import android.support.v4.app.Fragment; 25 | import android.support.v4.app.FragmentManager; 26 | import android.support.v4.app.FragmentPagerAdapter; 27 | import android.support.v4.view.ViewPager; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.support.v7.widget.Toolbar; 30 | import android.text.Html; 31 | import android.view.Menu; 32 | import android.view.MenuItem; 33 | import android.view.View; 34 | import android.widget.TextView; 35 | 36 | import eu.chainfire.suhide.AppAdapter.AppItem; 37 | 38 | public class 39 | MainActivity 40 | extends 41 | AppCompatActivity 42 | implements 43 | AppFragment.OnListFragmentInteractionListener, 44 | MyApplication.OnStartupListener 45 | { 46 | private SectionsPagerAdapter sectionsPagerAdapter; 47 | private ViewPager viewPager; 48 | private TabLayout tabLayout; 49 | 50 | private MyApplication application; 51 | 52 | @Override 53 | protected void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | setContentView(R.layout.activity_main); 56 | 57 | application = ((MyApplication)getApplication()); 58 | 59 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 60 | setSupportActionBar(toolbar); 61 | 62 | setTitle(R.string.main_title); 63 | 64 | sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); 65 | 66 | viewPager = (ViewPager) findViewById(R.id.container); 67 | viewPager.setAdapter(sectionsPagerAdapter); 68 | viewPager.setOffscreenPageLimit(3); 69 | viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 70 | @Override 71 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 72 | } 73 | 74 | @Override 75 | public void onPageSelected(int position) { 76 | invalidateOptionsMenu(); 77 | } 78 | 79 | @Override 80 | public void onPageScrollStateChanged(int state) { 81 | } 82 | }); 83 | 84 | tabLayout = (TabLayout) findViewById(R.id.tabs); 85 | tabLayout.setupWithViewPager(viewPager); 86 | 87 | application.setStartupListener(this); 88 | application.startup(); 89 | } 90 | 91 | private void clearStartup() { 92 | application.setStartupListener(null); 93 | application.clearStartup(); 94 | } 95 | 96 | @Override 97 | protected void onDestroy() { 98 | if (isFinishing()) { 99 | clearStartup(); 100 | } 101 | super.onDestroy(); 102 | } 103 | 104 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 105 | public SectionsPagerAdapter(FragmentManager fm) { 106 | super(fm); 107 | } 108 | 109 | @Override 110 | public Fragment getItem(int position) { 111 | Fragment ret = null; 112 | if (position == 0) { 113 | ret = AppFragment.newInstance(); 114 | } else if (position == 1) { 115 | ret = TextFragment.newInstance(); 116 | } 117 | ret.setRetainInstance(true); 118 | return ret; 119 | } 120 | 121 | @Override 122 | public int getCount() { 123 | return 2; 124 | } 125 | 126 | @Override 127 | public CharSequence getPageTitle(int position) { 128 | switch (position) { 129 | case 0: 130 | return "Apps"; 131 | case 1: 132 | return "About"; 133 | } 134 | return null; 135 | } 136 | } 137 | 138 | @Override 139 | public boolean onCreateOptionsMenu(Menu menu) { 140 | if (viewPager.getCurrentItem() == 0) { 141 | MenuItem item = menu.add(R.string.btn_kill); 142 | item.setIcon(R.drawable.ic_kill); 143 | item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 144 | item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 145 | @Override 146 | public boolean onMenuItemClick(MenuItem menuItem) { 147 | performKill(); 148 | return true; 149 | } 150 | }); 151 | } 152 | return true; 153 | } 154 | 155 | @Override 156 | protected void onUserLeaveHint() { 157 | finish(); 158 | clearStartup(); 159 | super.onUserLeaveHint(); 160 | } 161 | 162 | @Override 163 | public void onBackPressed() { 164 | finish(); 165 | clearStartup(); 166 | super.onBackPressed(); 167 | } 168 | 169 | @Override 170 | public void onListFragmentInteraction(AppItem item) { 171 | int state = item.nextState(true); 172 | String title = item.title; 173 | if (title == null) title = item.packageName; 174 | title = "" + title + ""; 175 | int resId = 0; 176 | switch (state) { 177 | case AppItem.ROOT: resId = R.string.set_state_root; break; 178 | case AppItem.NO_ROOT: resId = R.string.set_state_no_root; break; 179 | case AppItem.HIDDEN: resId = R.string.set_state_hidden; break; 180 | } 181 | Snackbar.make(viewPager, Html.fromHtml(getString(resId, title)), Snackbar.LENGTH_SHORT).show(); 182 | application.save(); 183 | } 184 | 185 | @Override 186 | public void onStartupProgressMessage(String message) { 187 | ((TextView)findViewById(R.id.progress_text)).setText(message); 188 | } 189 | 190 | @Override 191 | public void onStartupErrorMessage(String message) { 192 | findViewById(R.id.progress_bar).setVisibility(View.GONE); 193 | findViewById(R.id.progress_error).setVisibility(View.VISIBLE); 194 | ((TextView)findViewById(R.id.progress_text)).setText(message); 195 | } 196 | 197 | @Override 198 | public void onStartupComplete() { 199 | findViewById(R.id.progress_container).setVisibility(View.GONE); 200 | findViewById(R.id.tabs).setVisibility(View.VISIBLE); 201 | findViewById(R.id.container).setVisibility(View.VISIBLE); 202 | } 203 | 204 | private class KillTask extends AsyncTask { 205 | private ProgressDialog progress = null; 206 | 207 | @Override 208 | protected void onPreExecute() { 209 | try { 210 | progress = new ProgressDialog(MainActivity.this); 211 | progress.setIndeterminate(true); 212 | progress.setCancelable(false); 213 | progress.setTitle(R.string.kill_title); 214 | progress.setMessage(getString(R.string.kill_message)); 215 | progress.show(); 216 | } catch (Exception e) { 217 | e.printStackTrace(); 218 | } 219 | } 220 | 221 | @Override 222 | protected Void doInBackground(Void... voids) { 223 | application.kill(true); 224 | return null; 225 | } 226 | 227 | @Override 228 | protected void onPostExecute(Void aVoid) { 229 | try { 230 | progress.dismiss(); 231 | } catch (Exception e) { 232 | e.printStackTrace(); 233 | } 234 | } 235 | } 236 | 237 | private void performKill() { 238 | (new KillTask()).execute(); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /suhide/src/main/java/eu/chainfire/suhide/MyApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.chainfire.suhide; 18 | 19 | import android.app.Application; 20 | import android.os.Handler; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import eu.chainfire.libsuperuser.Debug; 26 | import eu.chainfire.libsuperuser.Shell; 27 | 28 | //TODO move startup stuff to its own class 29 | 30 | public class MyApplication extends Application { 31 | public interface OnStartupListener { 32 | void onStartupProgressMessage(String message); 33 | void onStartupErrorMessage(String message); 34 | void onStartupComplete(); 35 | } 36 | 37 | private final Handler handler = new Handler(); 38 | private Thread startup = null; 39 | 40 | private boolean startingUp = false; 41 | private String startupProgress = null; 42 | private String startupError = null; 43 | private boolean startupComplete = false; 44 | private OnStartupListener startupListener = null; 45 | 46 | private AppAdapter appAdapter = new AppAdapter(); 47 | 48 | private Shell.Interactive rootShell = null; 49 | 50 | private List suhideUid = new ArrayList(); 51 | private List suhidePkg = new ArrayList(); 52 | 53 | @Override 54 | public void onCreate() { 55 | super.onCreate(); 56 | Debug.setDebug(BuildConfig.DEBUG); 57 | startup(); 58 | } 59 | 60 | public void startup() { 61 | if (startingUp) return; 62 | startingUp = true; 63 | 64 | startup = new Thread(new Runnable() { 65 | @Override 66 | public void run() { 67 | asyncStartup(); 68 | startup = null; 69 | } 70 | }); 71 | startup.start(); 72 | } 73 | 74 | public void clearStartup() { 75 | startingUp = false; 76 | startupProgress = null; 77 | startupError = null; 78 | startupComplete = false; 79 | } 80 | 81 | public AppAdapter getAppAdapter() { 82 | return appAdapter; 83 | } 84 | 85 | public Shell.Interactive getRootShell() { 86 | return rootShell; 87 | } 88 | 89 | public void setStartupListener(OnStartupListener listener) { 90 | this.startupListener = listener; 91 | if (listener != null) { 92 | if (startupComplete) { 93 | listener.onStartupComplete(); 94 | } else if (startupError != null) { 95 | listener.onStartupErrorMessage(startupError); 96 | } else if (startupProgress != null) { 97 | listener.onStartupProgressMessage(startupProgress); 98 | } 99 | } 100 | } 101 | 102 | private void setStartupProgress(final String message) { 103 | handler.post(new Runnable() { 104 | @Override 105 | public void run() { 106 | startupProgress = message; 107 | if (startupListener != null) { 108 | startupListener.onStartupProgressMessage(message); 109 | } 110 | } 111 | }); 112 | } 113 | 114 | private void setStartupError(final String message) { 115 | handler.post(new Runnable() { 116 | @Override 117 | public void run() { 118 | startupError = message; 119 | if (startupListener != null) { 120 | startupListener.onStartupErrorMessage(message); 121 | } 122 | } 123 | }); 124 | } 125 | 126 | private void setStartupComplete() { 127 | handler.post(new Runnable() { 128 | @Override 129 | public void run() { 130 | startupComplete = true; 131 | if (startupListener != null) { 132 | startupListener.onStartupComplete(); 133 | } 134 | } 135 | }); 136 | } 137 | 138 | private void asyncStartup() { 139 | setStartupProgress(getString(R.string.startup_loading)); 140 | 141 | setStartupProgress(getString(R.string.startup_getting_root)); 142 | final boolean[] suGranted = { false }; 143 | rootShell = (new Shell.Builder()) 144 | .useSU() 145 | .addCommand("id", 0, new Shell.OnCommandResultListener() { 146 | @Override 147 | public void onCommandResult(int commandCode, int exitCode, List output) { 148 | synchronized (suGranted) { 149 | suGranted[0] = true; 150 | } 151 | } 152 | }) 153 | .open(new Shell.OnCommandResultListener() { 154 | @Override 155 | public void onCommandResult(int commandCode, int exitCode, List output) { 156 | } 157 | }); 158 | rootShell.waitForIdle(); 159 | if (!suGranted[0]) { 160 | setStartupError(getString(R.string.error_no_root)); 161 | return; 162 | } 163 | 164 | setStartupProgress(getString(R.string.startup_detecting_root_state)); 165 | final boolean[] isSuperSU = { false }; 166 | rootShell.addCommand("su -v", 0, new Shell.OnCommandResultListener() { 167 | @Override 168 | public void onCommandResult(int commandCode, int exitCode, List output) { 169 | for (String line : output) { 170 | if (line.toLowerCase().contains("supersu")) { 171 | isSuperSU[0] = true; 172 | } 173 | } 174 | } 175 | }); 176 | rootShell.waitForIdle(); 177 | if (!isSuperSU[0]) { 178 | setStartupError(getString(R.string.error_no_supersu)); 179 | return; 180 | } 181 | 182 | final boolean[] haveLink = { false }; 183 | rootShell.addCommand("readlink /sbin/supersu_link", 0, new Shell.OnCommandResultListener() { 184 | @Override 185 | public void onCommandResult(int commandCode, int exitCode, List output) { 186 | for (String line : output) { 187 | if (line.contains("/data")) { 188 | haveLink[0] = true; 189 | } 190 | } 191 | } 192 | }); 193 | rootShell.waitForIdle(); 194 | if (!haveLink[0]) { 195 | setStartupError(getString(R.string.error_no_sbin_bind)); 196 | return; 197 | } 198 | 199 | setStartupProgress(getString(R.string.startup_reading_config)); 200 | final boolean[] haveSuHide = { false }; 201 | rootShell.addCommand("ls -d /sbin/supersu/suhide", 0, new Shell.OnCommandResultListener() { 202 | @Override 203 | public void onCommandResult(int commandCode, int exitCode, List output) { 204 | for (String line : output) { 205 | if (line.contains("/suhide")) { 206 | haveSuHide[0] = true; 207 | } 208 | } 209 | } 210 | }); 211 | rootShell.waitForIdle(); 212 | if (!haveSuHide[0]) { 213 | setStartupError(getString(R.string.error_no_suhide)); 214 | return; 215 | } 216 | 217 | suhideUid.clear(); 218 | rootShell.addCommand("cat /sbin/supersu/suhide/suhide.uid", 0, new Shell.OnCommandResultListener() { 219 | @Override 220 | public void onCommandResult(int commandCode, int exitCode, List output) { 221 | for (String line : output) { 222 | line = line.trim(); 223 | if (line.length() > 0) { 224 | suhideUid.add(line); 225 | } 226 | } 227 | } 228 | }); 229 | suhidePkg.clear(); 230 | rootShell.addCommand("cat /sbin/supersu/suhide/suhide.pkg", 0, new Shell.OnCommandResultListener() { 231 | @Override 232 | public void onCommandResult(int commandCode, int exitCode, List output) { 233 | for (String line : output) { 234 | line = line.trim(); 235 | if (line.length() > 0) { 236 | suhidePkg.add(line); 237 | } 238 | } 239 | } 240 | }); 241 | 242 | setStartupProgress(getString(R.string.startup_detecting_apps)); 243 | appAdapter.loadAppItems(this); 244 | 245 | for (int i = suhideUid.size() - 1; i >= 0; i--) { 246 | boolean found = false; 247 | for (int j = 0; j < appAdapter.getItemCount(); j++) { 248 | String uid = suhideUid.get(i); 249 | AppAdapter.AppItem appItem = appAdapter.getItem(j); 250 | if (uid.equals(appItem.packageName) || uid.equals(String.valueOf(appItem.uid))) { 251 | appAdapter.getItem(j).setState(AppAdapter.AppItem.NO_ROOT); 252 | found = true; 253 | } 254 | } 255 | if (found) suhideUid.remove(i); 256 | } 257 | for (int i = suhidePkg.size() - 1; i >= 0; i--) { 258 | for (int j = 0; j < appAdapter.getItemCount(); j++) { 259 | if (suhidePkg.get(i).equals(appAdapter.getItem(j).packageName)) { 260 | appAdapter.getItem(j).setState(AppAdapter.AppItem.HIDDEN); 261 | suhidePkg.remove(i); 262 | break; 263 | } 264 | } 265 | } 266 | 267 | setStartupProgress(getString(R.string.startup_complete)); 268 | setStartupComplete(); 269 | } 270 | 271 | public void save() { 272 | List commands = new ArrayList(); 273 | commands.add("rm /sbin/supersu/suhide/suhide.uid"); 274 | commands.add("rm /sbin/supersu/suhide/suhide.pkg"); 275 | 276 | List listed = new ArrayList(); 277 | 278 | for (String line : suhideUid) { 279 | // these are actual uids or partial names, this GUI doesn't handle those, 280 | // but we do save them if they were present during load 281 | listed.add(line); 282 | commands.add("echo \"" + line + "\">>/sbin/supersu/suhide/suhide.uid"); 283 | } 284 | for (String line : suhidePkg) { 285 | commands.add("echo \"" + line + "\">>/sbin/supersu/suhide/suhide.pkg"); 286 | } 287 | 288 | for (int i = 0; i < appAdapter.getItemCount(); i++) { 289 | AppAdapter.AppItem appItem = appAdapter.getItem(i); 290 | if (appItem.state == AppAdapter.AppItem.NO_ROOT) { 291 | String s = String.valueOf(appItem.uid); 292 | if (!listed.contains(s)) { 293 | commands.add("echo \"" + s + "\">>/sbin/supersu/suhide/suhide.uid"); 294 | listed.add(s); 295 | } 296 | } else if (appItem.state == AppAdapter.AppItem.HIDDEN) { 297 | commands.add("echo \"" + appItem.packageName + "\">>/sbin/supersu/suhide/suhide.pkg"); 298 | } 299 | } 300 | 301 | commands.add("chmod 0600 /sbin/supersu/suhide/suhide.uid"); 302 | commands.add("chmod 0600 /sbin/supersu/suhide/suhide.pkg"); 303 | commands.add("chcon u:object_r:system_file:s0 /sbin/supersu/suhide/suhide.uid"); 304 | commands.add("chcon u:object_r:system_file:s0 /sbin/supersu/suhide/suhide.pkg"); 305 | 306 | rootShell.addCommand(commands); 307 | } 308 | 309 | public void kill(boolean wait) { 310 | rootShell.addCommand(new String[] { 311 | "for PID in `ls /proc`; do\n" + 312 | " if [ -f \"/proc/$PID/cmdline\" ]; then\n" + 313 | " CMDLINE=$(cat /proc/$PID/cmdline | tr -d '\\000')\n" + 314 | " UID=$(cat /proc/$PID/status | grep -i uid)\n" + 315 | " if [ ! -z \"$CMDLINE\" ]; then\n" + 316 | " for ENTRY in `cat /sbin/supersu/suhide/suhide.uid`; do\n" + 317 | " if (`echo \"$ENTRY\" | grep \"\\.\" >/dev/null`); then\n" + 318 | " if (`echo \"$CMDLINE\" | grep \"^$ENTRY\" >/dev/null`); then\n" + 319 | " if (`echo \"$CMDLINE\" | grep \"^$ENTRY$\" >/dev/null`) || (`echo \"$CMDLINE\" | grep \"^$ENTRY:\" >/dev/null`); then\n" + 320 | " echo KILLING PACKAGE $PID $ENTRY\n" + 321 | " kill -9 $PID\n" + 322 | " fi\n" + 323 | " fi\n" + 324 | " else\n" + 325 | " if (`echo \"$UID\" | grep \"$ENTRY\" >/dev/null`); then\n" + 326 | " echo KILLING UID $PID $ENTRY\n" + 327 | " kill -9 $PID\n" + 328 | " fi\n" + 329 | " fi\n" + 330 | " done\n" + 331 | " fi\n" + 332 | " fi\n" + 333 | "done\n" 334 | }); 335 | 336 | if (wait) { 337 | rootShell.waitForIdle(); 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /suhide/src/main/java/eu/chainfire/suhide/TextFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Jorrit "Chainfire" Jongma & CCMT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package eu.chainfire.suhide; 18 | 19 | import android.content.pm.PackageInfo; 20 | import android.content.pm.PackageManager; 21 | import android.os.Bundle; 22 | import android.support.v4.app.Fragment; 23 | import android.text.Html; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.TextView; 28 | 29 | public class TextFragment extends Fragment { 30 | public TextFragment() { 31 | } 32 | 33 | public static TextFragment newInstance() { 34 | TextFragment fragment = new TextFragment(); 35 | return fragment; 36 | } 37 | 38 | @Override 39 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 40 | Bundle savedInstanceState) { 41 | View rootView = inflater.inflate(R.layout.fragment_main, container, false); 42 | TextView textView = (TextView) rootView.findViewById(R.id.about); 43 | 44 | String version = "?.??"; 45 | PackageManager pm = getContext().getPackageManager(); 46 | if (pm != null) { 47 | try { 48 | PackageInfo pi = pm.getPackageInfo(getContext().getPackageName(), 0); 49 | if (pi != null) { 50 | version = pi.versionName; 51 | } 52 | } catch (Exception e) { 53 | } 54 | } 55 | 56 | textView.setText(Html.fromHtml(getString(R.string.about).replace("X.YY", version))); 57 | return rootView; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /suhide/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /suhide/src/main/res/drawable/ic_hidden.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /suhide/src/main/res/drawable/ic_kill.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /suhide/src/main/res/drawable/ic_no_root.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /suhide/src/main/res/drawable/ic_root.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /suhide/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 23 | 24 | 25 | 33 | 34 | 35 | 43 | 44 | 49 | 50 | 56 | 57 | 64 | 65 | 66 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /suhide/src/main/res/layout/fragment_app.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 25 | 26 | 34 | 35 | 43 | 44 | 45 | 52 | 53 | -------------------------------------------------------------------------------- /suhide/src/main/res/layout/fragment_app_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /suhide/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /suhide/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /suhide/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /suhide/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /suhide/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /suhide/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chainfire/suhide-lite/49369367bc09a93133db9bf0a1b8cbce7ace6229/suhide/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /suhide/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /suhide/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /suhide/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #004d40 4 | #002620 5 | #009688 6 | 7 | -------------------------------------------------------------------------------- /suhide/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 8dp 7 | 16dp 8 | 9 | -------------------------------------------------------------------------------- /suhide/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | suhide 3 | suhide-lite 4 | Root allowed for %s 5 | Root hidden from %s 6 | Hiding %s from other apps 7 | Loading… 8 | Acquiring root access… 9 | Detecting root state… 10 | Reading suhide configuration… 11 | Detecting apps… 12 | Complete! 13 | Could not acquire root access! 14 | Root is not SuperSU! 15 | SuperSU is not running in SBIN mode! 16 | Could not find suhide binaries! 17 | Kill 18 | Killing… 19 | Terminating all running processes that should not be able to see root, please wait… 20 | 21 | suhide vX.YY
23 | Copyright © 2016-2017 Chainfire & CCMT
24 |
25 | Basic operation
26 | You can configure each app if it should be able to see root (green 27 | icon), if root should be hidden from it (red icon), or if the app 28 | should be hidden from other apps (blue icon).
29 |
30 | For performance reasons it is best to allow apps to have root (green 31 | icon) by default.
32 |
33 | Killing apps
34 | Hiding root from an app only goes into effect the next time the app 35 | is started. This user interface does not kill apps automatically when 36 | you select them, because it is a slow operation. The cross icon in the 37 | app\'s toolbar allows you to manually kill all running instances of apps 38 | that root should be hidden from, once you are done configuring.
39 |
40 | Hiding apps
41 | Some apps (such as games) detect the presence of other apps (such 42 | as SuperSU). By pressing volume up, volume down, volume up, 43 | volume down, volume up, volume down (so three times alternated 44 | volume up, volume down) in less than 3 seconds, the selected 45 | apps will be hidden or unhidden from all other apps (including the 46 | launcher). In this hidden state, SuperSU will keep granting root to 47 | already configured apps, and will deny root from all other apps. 48 | ]]>
49 |
50 | -------------------------------------------------------------------------------- /suhide/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 16 | 17 |