├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── README.md ├── build.gradle ├── build.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── magisk ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script ├── config ├── customize.sh └── module.prop ├── module ├── .gitignore ├── build.gradle ├── jni │ ├── Android.mk │ ├── Application.mk │ ├── hook.cpp │ ├── hook.h │ ├── logging.h │ ├── main.cpp │ ├── server.cpp │ ├── server.h │ ├── util.h │ └── zygisk.hpp └── src │ └── main │ └── AndroidManifest.xml └── settings.gradle /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: 'recursive' 19 | 20 | - name: Setup Android NDK 21 | uses: nttld/setup-ndk@v1 22 | with: 23 | ndk-version: r25b 24 | add-to-path: true 25 | local-cache: false 26 | 27 | - name: Build 28 | run: ./build.sh 29 | 30 | - name: Collect artifcat name 31 | run: | 32 | echo "release=$(basename -s .zip build/*.zip)" >> $GITHUB_ENV 33 | 34 | - name: Upload Release 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: ${{ env.release }} 38 | path: magisk 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | magisk/zygisk/*.so -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libcxx"] 2 | path = module/jni/libcxx 3 | url = https://github.com/topjohnwu/libcxx.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiPush Zygisk 2 | 3 | 一个用于为应用伪装小米设备的 Magisk/Zygisk 模块,以便于使用 [MiPush](https://github.com/NihilityT/MiPushFramework.git) 4 | 5 | ### 下载 6 | 前往 [Release](https://github.com/notxx/MiPushZygisk/releases) 下载 7 | 8 | ### 配置 9 | 手动编辑 `/data/adb/modules/zygisk_mipushfake/config` 文件 10 | 11 | 配置示例: 12 | ``` 13 | #对该应用所有进程进行伪装 14 | com.example.app 15 | 16 | #对该应用指定进程进行伪装,格式:包名|进程名 17 | com.example.app|com.example.app:push 18 | ``` 19 | 20 | 21 | ### 构建 22 | 配置 ndk 路径后在项目根目录执行,构建产物在 `build` 目录下 23 | ```shell 24 | ./build.sh 25 | ``` 26 | 27 | ### 致谢 28 | 感谢[HMSPush](https://github.com/fei-ke/HmsPushZygisk.git),这个项目99%的代码都是从那边抄的 29 | 30 | ### License 31 | [GNU General Public License v3 (GPL-3)](http://www.gnu.org/copyleft/gpl.html)。 32 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.0.3" 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | task clean(type: Delete) { 16 | delete rootProject.buildDir 17 | } 18 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | ndk-build -C module || exit 1 6 | 7 | mkdir -p magisk/zygisk 8 | 9 | for arch in arm64-v8a armeabi-v7a x86 x86_64 10 | do 11 | cp module/libs/$arch/*.so magisk/zygisk/$arch.so 12 | done 13 | 14 | pushd magisk 15 | 16 | version=`grep '^version=' module.prop | cut -d= -f2` 17 | name=`grep '^id=' module.prop | cut -d= -f2` 18 | 19 | rm -rf ../build 20 | mkdir -p ../build 21 | 22 | zip -r9 ../build/mipush-zygisk-$version.zip . 23 | popd 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DogEasy/MiPushZygisk/7aa71049a7397fb70ac013f826458ee21807a511/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 26 02:13:18 PDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /magisk/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 -------------------------------------------------------------------------------- /magisk/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /magisk/config: -------------------------------------------------------------------------------- 1 | cmb.pb 2 | cn.adidas.app 3 | cn.adidas.confirmed.app 4 | cn.com.hkgt.gasapp 5 | cn.gov.tax.its 6 | cn.wps.moffice_eng 7 | com.alibaba.android.rimet 8 | com.alicloud.databox 9 | com.android.calendar 10 | com.android.email 11 | com.autonavi.minimap 12 | com.baidu.BaiduMap 13 | com.baidu.netdisk.samsung 14 | com.baidu.tieba 15 | com.bankcomm.maidanba 16 | com.cainiao.wireless 17 | com.cebbank.mobile.cemb 18 | com.chaoxing.mobile 19 | com.chinamobile.mcloud 20 | com.chinamworld.bocmbci 21 | com.chinamworld.main 22 | com.chinatelecom.bestpayclient 23 | com.cib.cibmb 24 | com.cmbchina.ccd.pluto.cmbActivity 25 | com.cn21.ecloud 26 | com.coolapk.market 27 | com.csii.hkb 28 | com.ct.client 29 | com.cubic.autohome 30 | com.dianping.v1 31 | com.digitalchina.mobile.dfhfz1 32 | com.dmzj.manhua 33 | com.dmzjsq.manhua 34 | com.douban.frodo 35 | com.dragon.read 36 | com.duoduo.tuanzhang 37 | com.duokan.phone.remotecontroller 38 | com.eg.android.AlipayGphone 39 | com.faloo.BookReader4Android 40 | com.greenpoint.android.mc10086.activity 41 | com.heytap.health 42 | com.hpbr.bosszhipin 43 | com.htinns 44 | com.icbc 45 | com.jd.jdlite 46 | com.jingdong.app.mall 47 | com.job.android 48 | com.kuangxiangciweimao.novel 49 | com.linroid.zlive 50 | com.lphtsccft 51 | com.miui.weather2 52 | com.MobileTicket 53 | com.netease.cloudmusic 54 | com.novel.du 55 | com.nowcasting.activity 56 | com.nowcasting.activity 57 | com.qidian.QDReader 58 | com.qiyi.video 59 | com.readunion.ireader 60 | com.ruanmei.ithome 61 | com.sankuai.meituan 62 | com.sdu.didi.psnger 63 | com.sf.activity 64 | com.sfacg 65 | com.sgcc.wsgw.cn 66 | com.sina.weibo 67 | com.sitech.ac 68 | com.smzdm.client.android 69 | com.ss.android.article.news 70 | com.ss.android.ugc.aweme 71 | com.starbucks.cn 72 | com.suning.mobile.ebuy 73 | com.t3go.passenger 74 | com.taobao.etao 75 | com.taobao.idelfish 76 | com.taobao.movie.android 77 | com.taobao.taobao 78 | com.taobao.trip 79 | com.tencent.androidqqmail 80 | com.tencent.mobileqq 81 | com.tencent.mp 82 | com.tencent.qqlive 83 | com.tencent.qqmusic 84 | com.tencent.tmgp.sgame 85 | com.tencent.wemeet.app 86 | com.tencent.weread 87 | com.timanetworks.android.faw.vw.aftermarket.release 88 | com.tmall.wireless 89 | com.tmri.app.main 90 | com.umetrip.android.msky.app 91 | com.unionpay 92 | com.weico.international 93 | com.whrcbank.mbank 94 | com.wudaokou.hippo 95 | com.xiachufang 96 | com.xiaomi.hm.health 97 | com.xiaomi.wearable 98 | com.xingin.xhs 99 | com.xunlei.downloadprovider 100 | com.xunmeng.pinduoduo 101 | com.xunyou.rb 102 | com.yingyonghui.market 103 | com.youdao.translator 104 | com.youdu 105 | com.youku.phone 106 | com.yueme.itv 107 | com.zhaopin.social 108 | com.zhihu.android 109 | ctrip.android.view 110 | hanjie.app.pureweather 111 | info.muge.appshare 112 | me.ele 113 | net.evecom.android.mztapp 114 | nutstore.android 115 | today.tophub.app 116 | top.tdtt.news 117 | tv.danmaku.bili 118 | -------------------------------------------------------------------------------- /magisk/customize.sh: -------------------------------------------------------------------------------- 1 | echo "Welcome to MiPush!" 2 | -------------------------------------------------------------------------------- /magisk/module.prop: -------------------------------------------------------------------------------- 1 | id=zygisk_mipushfake 2 | name=Zygisk - MiPushFake 3 | version=v1 4 | versionCode=10000 5 | author=fei-ke, notXX, Dog_EZ 6 | description=为应用伪装小米设备,以便使用 MiPushFramework 7 | -------------------------------------------------------------------------------- /module/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /libs 3 | /obj 4 | -------------------------------------------------------------------------------- /module/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | externalNativeBuild { 7 | ndkBuild { 8 | path("jni/Android.mk") 9 | } 10 | } 11 | ndkVersion '25.2.9519653' 12 | } 13 | -------------------------------------------------------------------------------- /module/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := mipush 5 | LOCAL_SRC_FILES := main.cpp server.cpp hook.cpp 6 | LOCAL_STATIC_LIBRARIES := libcxx 7 | LOCAL_LDLIBS := -llog 8 | include $(BUILD_SHARED_LIBRARY) 9 | 10 | include jni/libcxx/Android.mk 11 | 12 | # If you do not want to use libc++, link to system stdc++ 13 | # so that you can at least call the new operator in your code 14 | 15 | # include $(CLEAR_VARS) 16 | # LOCAL_MODULE := example 17 | # LOCAL_SRC_FILES := example.cpp 18 | # LOCAL_LDLIBS := -llog -lstdc++ 19 | # include $(BUILD_SHARED_LIBRARY) 20 | -------------------------------------------------------------------------------- /module/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 2 | APP_CPPFLAGS := -std=c++17 -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden 3 | APP_STL := none 4 | APP_PLATFORM := android-21 5 | -------------------------------------------------------------------------------- /module/jni/hook.cpp: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | #include "logging.h" 3 | #include 4 | 5 | using namespace std; 6 | 7 | jstring (*orig_native_get)(JNIEnv *env, jclass clazz, jstring keyJ, jstring defJ); 8 | 9 | jstring my_native_get(JNIEnv *env, jclass clazz, jstring keyJ, jstring defJ) { 10 | const char *key = env->GetStringUTFChars(keyJ, nullptr); 11 | const char *def = env->GetStringUTFChars(defJ, nullptr); 12 | 13 | jstring hooked_result = nullptr; 14 | 15 | // MIUI 16 | if (strcmp(key, "ro.product.brand") == 0) { // ro.product.brand=Xiaomi 17 | hooked_result = env->NewStringUTF("Xiaomi"); 18 | } else if (strcmp(key, "ro.product.manufacturer") == 0) { // ro.product.manufacturer=Xiaomi 19 | hooked_result = env->NewStringUTF("Xiaomi"); 20 | } else if (strcmp(key, "ro.miui.ui.version.name") == 0) { // ro.miui.ui.version.name=V130 21 | hooked_result = env->NewStringUTF("V130"); 22 | } else if (strcmp(key, "ro.miui.ui.version.code") == 0) { // ro.miui.ui.version.code=13 23 | hooked_result = env->NewStringUTF("13"); 24 | } else if (strcmp(key, "ro.miui.version.code_time") == 0) { // ro.miui.version.code_time=1658851200 25 | hooked_result = env->NewStringUTF("1658851200"); 26 | } else if (strcmp(key, "ro.miui.internal.storage") == 0) { // ro.miui.internal.storage=/sdcard/ 27 | hooked_result = env->NewStringUTF("/sdcard/"); 28 | } else if (strcmp(key, "ro.miui.region") == 0) { // ro.miui.region=CN 29 | hooked_result = env->NewStringUTF("CN"); 30 | } else if (strcmp(key, "ro.miui.cust_variant") == 0) { // ro.miui.cust_variant=cn 31 | hooked_result = env->NewStringUTF("cn"); 32 | } else if (strcmp(key, "ro.vendor.miui.region") == 0) { // ro.vendor.miui.region=CN 33 | hooked_result = env->NewStringUTF("CN"); 34 | } 35 | 36 | env->ReleaseStringUTFChars(keyJ, key); 37 | env->ReleaseStringUTFChars(defJ, def); 38 | 39 | if (hooked_result != nullptr) { 40 | return hooked_result; 41 | } else { 42 | return orig_native_get(env, clazz, keyJ, defJ); 43 | } 44 | } 45 | 46 | void hookBuild(JNIEnv *env) { 47 | LOGD("hook Build\n"); 48 | jclass build_class = env->FindClass("android/os/Build"); 49 | jstring new_brand = env->NewStringUTF("Xiaomi"); 50 | jstring new_manufacturer = env->NewStringUTF("Xiaomi"); 51 | 52 | jfieldID brand_id = env->GetStaticFieldID(build_class, "BRAND", "Ljava/lang/String;"); 53 | if (brand_id != nullptr) { 54 | env->SetStaticObjectField(build_class, brand_id, new_brand); 55 | } 56 | 57 | jfieldID manufacturer_id = env->GetStaticFieldID(build_class, "MANUFACTURER", "Ljava/lang/String;"); 58 | if (manufacturer_id != nullptr) { 59 | env->SetStaticObjectField(build_class, manufacturer_id, new_manufacturer); 60 | } 61 | 62 | env->DeleteLocalRef(new_brand); 63 | env->DeleteLocalRef(new_manufacturer); 64 | 65 | LOGD("hook Build done"); 66 | } 67 | 68 | void hookSystemProperties(JNIEnv *env, zygisk::Api *api) { 69 | LOGD("hook SystemProperties\n"); 70 | 71 | JNINativeMethod targetHookMethods[] = { 72 | {"native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", 73 | (void *) my_native_get}, 74 | }; 75 | 76 | api->hookJniNativeMethods(env, "android/os/SystemProperties", targetHookMethods, 1); 77 | 78 | *(void **) &orig_native_get = targetHookMethods[0].fnPtr; 79 | 80 | LOGD("hook SystemProperties done: %p\n", orig_native_get); 81 | } 82 | 83 | void Hook::hook() { 84 | hookBuild(env); 85 | hookSystemProperties(env, api); 86 | } 87 | -------------------------------------------------------------------------------- /module/jni/hook.h: -------------------------------------------------------------------------------- 1 | #ifndef MIPUSHZYGISK_HOOK_H 2 | #define MIPUSHZYGISK_HOOK_H 3 | 4 | 5 | #include 6 | #include 7 | #include "zygisk.hpp" 8 | 9 | using zygisk::Api; 10 | 11 | class Hook { 12 | public: 13 | Hook(Api *api, JNIEnv *env) { 14 | this->api = api; 15 | this->env = env; 16 | } 17 | 18 | void hook(); 19 | 20 | private: 21 | Api *api; 22 | JNIEnv *env; 23 | }; 24 | 25 | #endif //MIPUSHZYGISK_HOOK_H -------------------------------------------------------------------------------- /module/jni/logging.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOGGING_H 2 | #define _LOGGING_H 3 | 4 | #include 5 | #include 6 | 7 | #ifndef LOG_TAG 8 | #define LOG_TAG "MiPushZygisk" 9 | #endif 10 | 11 | #ifdef DEBUG 12 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 13 | #else 14 | #define LOGD(...) 15 | #endif 16 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 17 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 18 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 19 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 20 | #define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 21 | 22 | #endif // _LOGGING_H 23 | -------------------------------------------------------------------------------- /module/jni/main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 John "topjohnwu" Wu 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | * PERFORMANCE OF THIS SOFTWARE. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "zygisk.hpp" 21 | #include "logging.h" 22 | #include "server.h" 23 | #include "hook.h" 24 | #include "util.h" 25 | 26 | using zygisk::Api; 27 | using zygisk::AppSpecializeArgs; 28 | using zygisk::ServerSpecializeArgs; 29 | 30 | using namespace std; 31 | 32 | class MiPushZygisk : public zygisk::ModuleBase { 33 | public: 34 | void onLoad(Api *api, JNIEnv *env) override { 35 | this->api = api; 36 | this->env = env; 37 | } 38 | 39 | void preAppSpecialize(AppSpecializeArgs *args) override { 40 | string process_name = jstringToStdString(env, args->nice_name); 41 | string app_data_dir = jstringToStdString(env, args->app_data_dir); 42 | 43 | if (process_name.empty() || app_data_dir.empty()) { 44 | // Since we do not hook any functions, we should let Zygisk dlclose ourselves 45 | api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY); 46 | return; 47 | } 48 | 49 | string package_name = parsePackageName(app_data_dir.c_str()); 50 | 51 | LOGD("preAppSpecialize, packageName = %s, process = %s\n", package_name.c_str(), process_name.c_str()); 52 | 53 | preSpecialize(package_name, process_name); 54 | } 55 | 56 | void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { 57 | // Never tamper with system_server 58 | api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); 59 | } 60 | 61 | private: 62 | Api *api; 63 | JNIEnv *env; 64 | 65 | static string parsePackageName(const char *app_data_dir) { 66 | if (app_data_dir && *app_data_dir) { 67 | char package_name[256] = {0}; 68 | // /data/user// 69 | if (sscanf(app_data_dir, "/data/%*[^/]/%*[^/]/%s", package_name) == 1) { 70 | return package_name; 71 | } 72 | 73 | // /mnt/expand//user// 74 | if (sscanf(app_data_dir, "/mnt/expand/%*[^/]/%*[^/]/%*[^/]/%s", package_name) == 1) { 75 | return package_name; 76 | } 77 | 78 | // /data/data/ 79 | if (sscanf(app_data_dir, "/data/%*[^/]/%s", package_name) == 1) { 80 | return package_name; 81 | } 82 | } 83 | return ""; 84 | } 85 | 86 | void preSpecialize(const string &packageName, const string &process) { 87 | vector processList = requestRemoteConfig(packageName); 88 | if (!processList.empty()) { 89 | bool shouldHook = false; 90 | for (const auto &item: processList) { 91 | if (item.empty() || item == process) { 92 | shouldHook = true; 93 | break; 94 | } 95 | } 96 | 97 | if (shouldHook) { 98 | LOGI("hook+ package = [%s], process = [%s]\n", packageName.c_str(), process.c_str()); 99 | Hook(api, env).hook(); 100 | return; 101 | } else { 102 | LOGD("hook- package = [%s], process = [%s]\n", packageName.c_str(), process.c_str()); 103 | } 104 | } 105 | 106 | // Since we do not hook any functions, we should let Zygisk dlclose ourselves 107 | api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY); 108 | } 109 | 110 | /** 111 | * Request remote config from companion 112 | * @param packageName 113 | * @return list of processes to hook 114 | */ 115 | vector requestRemoteConfig(const string &packageName) { 116 | LOGD("requestRemoteConfig for %s", packageName.c_str()); 117 | auto fd = api->connectCompanion(); 118 | LOGD("connect to companion fd = %d", fd); 119 | vector content; 120 | 121 | auto size = receiveConfig(fd, content); 122 | auto configs = parseConfig(content, packageName); 123 | LOGD("Loaded module payload: %d bytes, config size:%lu ", size, configs.size()); 124 | 125 | close(fd); 126 | 127 | return configs; 128 | } 129 | 130 | static int receiveConfig(int remote_fd, vector &buf) { 131 | LOGD("start receiving config"); 132 | 133 | off_t size; 134 | int ret = read(remote_fd, &size, sizeof(size)); 135 | if (ret == 0) { 136 | LOGD("receive empty config"); 137 | return 0; 138 | } 139 | if (ret < 0) { 140 | LOGE("Failed to read size"); 141 | return -1; 142 | } 143 | 144 | buf.resize(size); 145 | 146 | int bytesReceived = 0; 147 | while (bytesReceived < size) { 148 | ret = read(remote_fd, buf.data() + bytesReceived, size - bytesReceived); 149 | if (ret < 0) { 150 | LOGE("Failed to read data"); 151 | return -1; 152 | } 153 | bytesReceived += ret; 154 | } 155 | 156 | // Ensure the last byte is '\n' 157 | if (!buf.empty() && buf[buf.size() - 1] != '\n') { 158 | buf.push_back('\n'); 159 | } 160 | return bytesReceived; 161 | } 162 | 163 | static vector parseConfig(const vector &content, const string &packageName) { 164 | vector result; 165 | 166 | if (content.empty()) return result; 167 | 168 | string line; 169 | for (char c: content) { 170 | if (c == '\n') { 171 | if (!line.empty() || line[0] != '#') { 172 | size_t delimiterPos = line.find('|'); 173 | bool found = delimiterPos != string::npos; 174 | auto pkg = line.substr(0, found ? delimiterPos : line.size()); 175 | if (pkg == packageName) { 176 | if (found) { 177 | result.push_back(line.substr(delimiterPos + 1, line.size())); 178 | } else { 179 | result.push_back(""); 180 | } 181 | } 182 | } 183 | line.clear(); 184 | } else { 185 | line.push_back(c); 186 | } 187 | } 188 | return result; 189 | } 190 | }; 191 | 192 | // Register our module class and the companion handler function 193 | REGISTER_ZYGISK_MODULE(MiPushZygisk) 194 | 195 | REGISTER_ZYGISK_COMPANION(Server::companion_handler) 196 | -------------------------------------------------------------------------------- /module/jni/server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "server.h" 7 | #include "logging.h" 8 | 9 | using namespace std; 10 | 11 | static constexpr auto CONFIG_PATH = "/data/adb/modules/zygisk_mipushfake/config"; 12 | 13 | off_t sendFile(int remote_fd, const string &path) { 14 | auto in_fd = open(path.c_str(), O_RDONLY); 15 | if (in_fd < 0) { 16 | LOGD("Failed to open file %s: %d (%s)", path.c_str(), errno, strerror(errno)); 17 | return -1; 18 | } 19 | 20 | auto size = lseek(in_fd, 0, SEEK_END); 21 | if (size < 0) { 22 | LOGE("Failed to get file size"); 23 | close(in_fd); 24 | return -1; 25 | } 26 | lseek(in_fd, 0, SEEK_SET); 27 | 28 | // Send size first for buffer allocation 29 | int ret = write(remote_fd, &size, sizeof(size)); 30 | if (ret < 0) { 31 | LOGI("Failed to send size"); 32 | close(in_fd); 33 | return -1; 34 | } 35 | 36 | ret = sendfile(remote_fd, in_fd, nullptr, size); 37 | if (ret < 0) { 38 | LOGI("Failed to send data"); 39 | close(in_fd); 40 | return -1; 41 | } 42 | 43 | close(in_fd); 44 | return size; 45 | } 46 | 47 | void Server::companion_handler(int remote_fd) { 48 | auto size = sendFile(remote_fd, (const string) CONFIG_PATH); 49 | LOGD("Sent module payload: %ld bytes", size); 50 | } -------------------------------------------------------------------------------- /module/jni/server.h: -------------------------------------------------------------------------------- 1 | #ifndef MIPUSHZYGISK_SERVER_H 2 | #define MIPUSHZYGISK_SERVER_H 3 | 4 | namespace Server { 5 | void companion_handler(int remote_fd); 6 | }; 7 | #endif //MIPUSHZYGISK_SERVER_H -------------------------------------------------------------------------------- /module/jni/util.h: -------------------------------------------------------------------------------- 1 | #ifndef MIPUSHZYGISK_UTIL_H 2 | #define MIPUSHZYGISK_UTIL_H 3 | 4 | #include 5 | 6 | static std::string jstringToStdString(JNIEnv *env, jstring &jstr) { 7 | if (env == nullptr || jstr == nullptr || !jstr) { 8 | return ""; 9 | } 10 | 11 | const char *chars = env->GetStringUTFChars(jstr, nullptr); 12 | if (chars && *chars) { 13 | std::string ret(chars); 14 | env->ReleaseStringUTFChars(jstr, chars); 15 | return ret; 16 | } 17 | return ""; 18 | } 19 | 20 | #endif //MIPUSHZYGISK_UTIL_H 21 | -------------------------------------------------------------------------------- /module/jni/zygisk.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2022-2023 John "topjohnwu" Wu 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | * PERFORMANCE OF THIS SOFTWARE. 13 | */ 14 | 15 | // This is the public API for Zygisk modules. 16 | // DO NOT MODIFY ANY CODE IN THIS HEADER. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #define ZYGISK_API_VERSION 4 23 | 24 | /* 25 | 26 | *************** 27 | * Introduction 28 | *************** 29 | 30 | On Android, all app processes are forked from a special daemon called "Zygote". 31 | For each new app process, zygote will fork a new process and perform "specialization". 32 | This specialization operation enforces the Android security sandbox on the newly forked 33 | process to make sure that 3rd party application code is only loaded after it is being 34 | restricted within a sandbox. 35 | 36 | On Android, there is also this special process called "system_server". This single 37 | process hosts a significant portion of system services, which controls how the 38 | Android operating system and apps interact with each other. 39 | 40 | The Zygisk framework provides a way to allow developers to build modules and run custom 41 | code before and after system_server and any app processes' specialization. 42 | This enable developers to inject code and alter the behavior of system_server and app processes. 43 | 44 | Please note that modules will only be loaded after zygote has forked the child process. 45 | THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON! 46 | 47 | ********************* 48 | * Development Guide 49 | ********************* 50 | 51 | Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. 52 | Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. 53 | 54 | Example code: 55 | 56 | static jint (*orig_logger_entry_max)(JNIEnv *env); 57 | static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } 58 | 59 | class ExampleModule : public zygisk::ModuleBase { 60 | public: 61 | void onLoad(zygisk::Api *api, JNIEnv *env) override { 62 | this->api = api; 63 | this->env = env; 64 | } 65 | void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { 66 | JNINativeMethod methods[] = { 67 | { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, 68 | }; 69 | api->hookJniNativeMethods(env, "android/util/Log", methods, 1); 70 | *(void **) &orig_logger_entry_max = methods[0].fnPtr; 71 | } 72 | private: 73 | zygisk::Api *api; 74 | JNIEnv *env; 75 | }; 76 | 77 | REGISTER_ZYGISK_MODULE(ExampleModule) 78 | 79 | ----------------------------------------------------------------------------------------- 80 | 81 | Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize, 82 | or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class 83 | never runs in a true superuser environment. 84 | 85 | If your module require access to superuser permissions, you can create and register 86 | a root companion handler function. This function runs in a separate root companion 87 | daemon process, and an Unix domain socket is provided to allow you to perform IPC between 88 | your target process and the root companion process. 89 | 90 | Example code: 91 | 92 | static void example_handler(int socket) { ... } 93 | 94 | REGISTER_ZYGISK_COMPANION(example_handler) 95 | 96 | */ 97 | 98 | namespace zygisk { 99 | 100 | struct Api; 101 | struct AppSpecializeArgs; 102 | struct ServerSpecializeArgs; 103 | 104 | class ModuleBase { 105 | public: 106 | 107 | // This method is called as soon as the module is loaded into the target process. 108 | // A Zygisk API handle will be passed as an argument. 109 | virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {} 110 | 111 | // This method is called before the app process is specialized. 112 | // At this point, the process just got forked from zygote, but no app specific specialization 113 | // is applied. This means that the process does not have any sandbox restrictions and 114 | // still runs with the same privilege of zygote. 115 | // 116 | // All the arguments that will be sent and used for app specialization is passed as a single 117 | // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app 118 | // process will be specialized. 119 | // 120 | // If you need to run some operations as superuser, you can call Api::connectCompanion() to 121 | // get a socket to do IPC calls with a root companion process. 122 | // See Api::connectCompanion() for more info. 123 | virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {} 124 | 125 | // This method is called after the app process is specialized. 126 | // At this point, the process has all sandbox restrictions enabled for this application. 127 | // This means that this method runs with the same privilege of the app's own code. 128 | virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} 129 | 130 | // This method is called before the system server process is specialized. 131 | // See preAppSpecialize(args) for more info. 132 | virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {} 133 | 134 | // This method is called after the system server process is specialized. 135 | // At this point, the process runs with the privilege of system_server. 136 | virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {} 137 | }; 138 | 139 | struct AppSpecializeArgs { 140 | // Required arguments. These arguments are guaranteed to exist on all Android versions. 141 | jint &uid; 142 | jint &gid; 143 | jintArray &gids; 144 | jint &runtime_flags; 145 | jobjectArray &rlimits; 146 | jint &mount_external; 147 | jstring &se_info; 148 | jstring &nice_name; 149 | jstring &instruction_set; 150 | jstring &app_data_dir; 151 | 152 | // Optional arguments. Please check whether the pointer is null before de-referencing 153 | jintArray *const fds_to_ignore; 154 | jboolean *const is_child_zygote; 155 | jboolean *const is_top_app; 156 | jobjectArray *const pkg_data_info_list; 157 | jobjectArray *const whitelisted_data_info_list; 158 | jboolean *const mount_data_dirs; 159 | jboolean *const mount_storage_dirs; 160 | 161 | AppSpecializeArgs() = delete; 162 | }; 163 | 164 | struct ServerSpecializeArgs { 165 | jint &uid; 166 | jint &gid; 167 | jintArray &gids; 168 | jint &runtime_flags; 169 | jlong &permitted_capabilities; 170 | jlong &effective_capabilities; 171 | 172 | ServerSpecializeArgs() = delete; 173 | }; 174 | 175 | namespace internal { 176 | struct api_table; 177 | template void entry_impl(api_table *, JNIEnv *); 178 | } 179 | 180 | // These values are used in Api::setOption(Option) 181 | enum Option : int { 182 | // Force Magisk's denylist unmount routines to run on this process. 183 | // 184 | // Setting this option only makes sense in preAppSpecialize. 185 | // The actual unmounting happens during app process specialization. 186 | // 187 | // Set this option to force all Magisk and modules' files to be unmounted from the 188 | // mount namespace of the process, regardless of the denylist enforcement status. 189 | FORCE_DENYLIST_UNMOUNT = 0, 190 | 191 | // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. 192 | // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory. 193 | // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. 194 | DLCLOSE_MODULE_LIBRARY = 1, 195 | }; 196 | 197 | // Bit masks of the return value of Api::getFlags() 198 | enum StateFlag : uint32_t { 199 | // The user has granted root access to the current process 200 | PROCESS_GRANTED_ROOT = (1u << 0), 201 | 202 | // The current process was added on the denylist 203 | PROCESS_ON_DENYLIST = (1u << 1), 204 | }; 205 | 206 | // All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded 207 | // from the specialized process afterwards. 208 | struct Api { 209 | 210 | // Connect to a root companion process and get a Unix domain socket for IPC. 211 | // 212 | // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions. 213 | // 214 | // The pre[XXX]Specialize methods run with the same privilege of zygote. 215 | // If you would like to do some operations with superuser permissions, register a handler 216 | // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). 217 | // Another good use case for a companion process is that if you want to share some resources 218 | // across multiple processes, hold the resources in the companion process and pass it over. 219 | // 220 | // The root companion process is ABI aware; that is, when calling this method from a 32-bit 221 | // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. 222 | // 223 | // Returns a file descriptor to a socket that is connected to the socket passed to your 224 | // module's companion request handler. Returns -1 if the connection attempt failed. 225 | int connectCompanion(); 226 | 227 | // Get the file descriptor of the root folder of the current module. 228 | // 229 | // This API only works in the pre[XXX]Specialize methods. 230 | // Accessing the directory returned is only possible in the pre[XXX]Specialize methods 231 | // or in the root companion process (assuming that you sent the fd over the socket). 232 | // Both restrictions are due to SELinux and UID. 233 | // 234 | // Returns -1 if errors occurred. 235 | int getModuleDir(); 236 | 237 | // Set various options for your module. 238 | // Please note that this method accepts one single option at a time. 239 | // Check zygisk::Option for the full list of options available. 240 | void setOption(Option opt); 241 | 242 | // Get information about the current process. 243 | // Returns bitwise-or'd zygisk::StateFlag values. 244 | uint32_t getFlags(); 245 | 246 | // Exempt the provided file descriptor from being automatically closed. 247 | // 248 | // This API only make sense in preAppSpecialize; calling this method in any other situation 249 | // is either a no-op (returns true) or an error (returns false). 250 | // 251 | // When false is returned, the provided file descriptor will eventually be closed by zygote. 252 | bool exemptFd(int fd); 253 | 254 | // Hook JNI native methods for a class 255 | // 256 | // Lookup all registered JNI native methods and replace it with your own methods. 257 | // The original function pointer will be saved in each JNINativeMethod's fnPtr. 258 | // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr 259 | // will be set to nullptr. 260 | void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); 261 | 262 | // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory. 263 | // 264 | // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example: 265 | // 266 | //
267 | // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64 268 | // (More details: https://man7.org/linux/man-pages/man5/proc.5.html) 269 | // 270 | // The `dev` and `inode` pair uniquely identifies a file being mapped into memory. 271 | // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`. 272 | // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. 273 | void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc); 274 | 275 | // Commit all the hooks that was previously registered. 276 | // Returns false if an error occurred. 277 | bool pltHookCommit(); 278 | 279 | private: 280 | internal::api_table *tbl; 281 | template friend void internal::entry_impl(internal::api_table *, JNIEnv *); 282 | }; 283 | 284 | // Register a class as a Zygisk module 285 | 286 | #define REGISTER_ZYGISK_MODULE(clazz) \ 287 | void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ 288 | zygisk::internal::entry_impl(table, env); \ 289 | } 290 | 291 | // Register a root companion request handler function for your module 292 | // 293 | // The function runs in a superuser daemon process and handles a root companion request from 294 | // your module running in a target process. The function has to accept an integer value, 295 | // which is a Unix domain socket that is connected to the target process. 296 | // See Api::connectCompanion() for more info. 297 | // 298 | // NOTE: the function can run concurrently on multiple threads. 299 | // Be aware of race conditions if you have globally shared resources. 300 | 301 | #define REGISTER_ZYGISK_COMPANION(func) \ 302 | void zygisk_companion_entry(int client) { func(client); } 303 | 304 | /********************************************************* 305 | * The following is internal ABI implementation detail. 306 | * You do not have to understand what it is doing. 307 | *********************************************************/ 308 | 309 | namespace internal { 310 | 311 | struct module_abi { 312 | long api_version; 313 | ModuleBase *impl; 314 | 315 | void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); 316 | void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); 317 | void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); 318 | void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); 319 | 320 | module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) { 321 | preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); }; 322 | postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); }; 323 | preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); }; 324 | postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); }; 325 | } 326 | }; 327 | 328 | struct api_table { 329 | // Base 330 | void *impl; 331 | bool (*registerModule)(api_table *, module_abi *); 332 | 333 | void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); 334 | void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); 335 | bool (*exemptFd)(int); 336 | bool (*pltHookCommit)(); 337 | int (*connectCompanion)(void * /* impl */); 338 | void (*setOption)(void * /* impl */, Option); 339 | int (*getModuleDir)(void * /* impl */); 340 | uint32_t (*getFlags)(void * /* impl */); 341 | }; 342 | 343 | template 344 | void entry_impl(api_table *table, JNIEnv *env) { 345 | static Api api; 346 | api.tbl = table; 347 | static T module; 348 | ModuleBase *m = &module; 349 | static module_abi abi(m); 350 | if (!table->registerModule(table, &abi)) return; 351 | m->onLoad(&api, env); 352 | } 353 | 354 | } // namespace internal 355 | 356 | inline int Api::connectCompanion() { 357 | return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1; 358 | } 359 | inline int Api::getModuleDir() { 360 | return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1; 361 | } 362 | inline void Api::setOption(Option opt) { 363 | if (tbl->setOption) tbl->setOption(tbl->impl, opt); 364 | } 365 | inline uint32_t Api::getFlags() { 366 | return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; 367 | } 368 | inline bool Api::exemptFd(int fd) { 369 | return tbl->exemptFd != nullptr && tbl->exemptFd(fd); 370 | } 371 | inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { 372 | if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods); 373 | } 374 | inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) { 375 | if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc); 376 | } 377 | inline bool Api::pltHookCommit() { 378 | return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); 379 | } 380 | 381 | } // namespace zygisk 382 | 383 | extern "C" { 384 | 385 | [[gnu::visibility("default"), maybe_unused]] 386 | void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); 387 | 388 | [[gnu::visibility("default"), maybe_unused]] 389 | void zygisk_companion_entry(int); 390 | 391 | } // extern "C" 392 | -------------------------------------------------------------------------------- /module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | include ':module' 9 | --------------------------------------------------------------------------------