├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── post.py ├── .gitignore ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── module ├── .gitignore ├── build.gradle.kts ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── cpp │ │ ├── CMakeLists.txt │ │ ├── logging │ │ ├── include │ │ │ └── logging.hpp │ │ └── logging.cpp │ │ └── zygisk │ │ ├── main.cpp │ │ └── zygisk.hpp └── template │ ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script │ ├── customize.sh │ ├── module.prop │ └── verify.sh └── settings.gradle.kts /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto eol=lf 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | *.bat text eol=crlf 6 | 7 | # Denote all files that are truly binary and should not be modified. 8 | *.so binary 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: self-host 13 | steps: 14 | - name: Check out 15 | uses: actions/checkout@v4 16 | with: 17 | submodules: "recursive" 18 | fetch-depth: 0 19 | 20 | - name: Setup Java 21 | uses: actions/setup-java@v4 22 | with: 23 | distribution: temurin 24 | java-version: 21 25 | 26 | - name: Setup Gradle 27 | uses: gradle/actions/setup-gradle@v3 28 | with: 29 | gradle-home-cache-cleanup: true 30 | 31 | - name: Setup Android SDK 32 | uses: android-actions/setup-android@v3 33 | with: 34 | packages: '' 35 | 36 | - name: Build with Gradle 37 | run: | 38 | ./gradlew zipRelease 39 | ./gradlew zipDebug 40 | 41 | - name: Package symbols and mappings 42 | run: | 43 | zip -r -9 "module/release/symbols-${{ github.sha }}.zip" module/build/symbols/ service/build/outputs/mapping/ 44 | 45 | - uses: actions/setup-python@v5 46 | with: 47 | python-version: '3.12' 48 | 49 | - name: Post to Telegram 50 | if: success() 51 | env: 52 | CHANNEL_ID: ${{ secrets.CHANNEL_ID }} 53 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 54 | EVENT_NAME: ${{ github.event_name }} 55 | COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 56 | COMMIT_URL: ${{ github.event.head_commit.url }} 57 | PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} 58 | PULL_REQUEST_BODY: ${{ github.event.pull_request.body }} 59 | PULL_REQUEST_URL: ${{ github.event.pull_request.html_url }} 60 | REF: ${{ github.ref }} 61 | SHA: ${{ github.sha }} 62 | run: | 63 | if [ -n "$BOT_TOKEN" ] && [ -n "$CHANNEL_ID" ]; then 64 | python3 -m pip install --upgrade telethon 65 | python3 .github/workflows/post.py module/release 66 | fi 67 | -------------------------------------------------------------------------------- /.github/workflows/post.py: -------------------------------------------------------------------------------- 1 | import os, asyncio, sys 2 | from pathlib import Path 3 | from telethon import TelegramClient, utils 4 | from telethon.tl.functions.help import GetConfigRequest 5 | 6 | 7 | API_ID = 611335 8 | API_HASH = "d524b414d21f4d37f08684c1df41ac9c" 9 | bot_token = os.environ["BOT_TOKEN"] 10 | channel_id = int(os.environ["CHANNEL_ID"]) 11 | event_name = os.environ["EVENT_NAME"] 12 | if event_name == "push": 13 | msg = f'**Push:** [{os.environ["SHA"][:7]}]({os.environ["COMMIT_URL"]})\n{os.environ["COMMIT_MESSAGE"]}' 14 | elif event_name == "pull_request": 15 | msg = f'**Pull request:** [{os.environ["PULL_REQUEST_TITLE"]}]({os.environ["PULL_REQUEST_URL"]})\n{os.environ["PULL_REQUEST_BODY"]}' 16 | elif event_name == "workflow_dispatch": 17 | msg = f'**Workflow dispatch** on `{os.environ["REF"]}`' 18 | 19 | 20 | async def main(): 21 | if len(sys.argv) < 2: 22 | raise ValueError("No files to upload") 23 | files = [] 24 | for file in list(Path(sys.argv[1]).glob("*.zip")): 25 | files.append(open(file, "rb")) 26 | caption = [""] * len(files) 27 | caption[-1] = msg[:1024] 28 | async with await TelegramClient( 29 | session="/tmp/znbot", api_id=API_ID, api_hash=API_HASH 30 | ).start(bot_token=bot_token) as bot: 31 | await bot.send_file(entity=channel_id, file=files, caption=caption) 32 | 33 | 34 | if __name__ == "__main__": 35 | try: 36 | asyncio.run(main()) 37 | except Exception as e: 38 | print(f"Error: {e}") 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build Vars Spoofing 2 | 3 | Build Vars Spoofing. **Android 8.1 or above is required**. 4 | 5 | ## Usage 6 | 7 | 1. Flash this module and reboot. 8 | 2. Enjoy! 9 | 10 | You can try enabling/disabling Build variable spoofing by creating/deleting the file `/data/adb/build_var_spoof/spoof_build_vars`. 11 | 12 | Build Vars Spoofing will automatically generate example config props inside `/data/adb/build_var_spoof/spoof_build_vars` once created, on next reboot, then you may manually edit your spoof config. 13 | 14 | Here is an example of a spoof config: 15 | 16 | ``` 17 | MANUFACTURER=Google 18 | MODEL=Pixel 9 Pro 19 | FINGERPRINT=google/caiman/caiman:14/AD1A.240530.047.U1/12150698:user/release-keys 20 | BRAND=google 21 | PRODUCT=caiman 22 | DEVICE=caiman 23 | RELEASE=14 24 | ID=AD1A.240530.047.U1 25 | INCREMENTAL=12150698 26 | TYPE=user 27 | TAGS=release-keys 28 | SECURITY_PATCH=2024-08-05 29 | ``` 30 | 31 | ## Acknowledgement 32 | 33 | - [PlayIntegrityFix](https://github.com/chiteroman/PlayIntegrityFix) 34 | - [LSPosed](https://github.com/LSPosed/LSPosed) 35 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.AppExtension 2 | import java.io.ByteArrayOutputStream 3 | 4 | plugins { 5 | alias(libs.plugins.agp.app) apply false 6 | alias(libs.plugins.jetbrains.kotlin.android) apply false 7 | } 8 | 9 | fun String.execute(currentWorkingDir: File = file("./")): String { 10 | val byteOut = ByteArrayOutputStream() 11 | project.exec { 12 | workingDir = currentWorkingDir 13 | commandLine = split("\\s".toRegex()) 14 | standardOutput = byteOut 15 | } 16 | return String(byteOut.toByteArray()).trim() 17 | } 18 | 19 | val gitCommitCount = "git rev-list HEAD --count".execute().toInt() 20 | val gitCommitHash = "git rev-parse --verify --short HEAD".execute() 21 | 22 | // also the soname 23 | val moduleId by extra("build_var_spoof") 24 | val moduleName by extra("Build var Spoof") 25 | val author by extra("LSPosed Developers") 26 | val description by extra("Build Vars Spoofing") 27 | val verName by extra("v1.0.0") 28 | val verCode by extra(gitCommitCount) 29 | val commitHash by extra(gitCommitHash) 30 | val abiList by extra(listOf("arm64-v8a", "x86_64", "armeabi-v7a", "x86")) 31 | 32 | val androidMinSdkVersion by extra(27) 33 | val androidTargetSdkVersion by extra(34) 34 | val androidCompileSdkVersion by extra(34) 35 | val androidBuildToolsVersion by extra("34.0.0") 36 | val androidCompileNdkVersion by extra("27.0.12077973") 37 | val androidSourceCompatibility by extra(JavaVersion.VERSION_17) 38 | val androidTargetCompatibility by extra(JavaVersion.VERSION_17) 39 | 40 | tasks.register("Delete", Delete::class) { 41 | delete(layout.buildDirectory) 42 | } 43 | 44 | subprojects { 45 | plugins.withId("com.android.application") { 46 | extensions.configure(AppExtension::class.java) { 47 | compileSdkVersion(androidCompileSdkVersion) 48 | ndkVersion = androidCompileNdkVersion 49 | buildToolsVersion = androidBuildToolsVersion 50 | 51 | defaultConfig { 52 | minSdk = androidMinSdkVersion 53 | targetSdk = androidCompileSdkVersion 54 | versionCode = verCode 55 | versionName = verName 56 | } 57 | 58 | compileOptions { 59 | sourceCompatibility = androidSourceCompatibility 60 | targetCompatibility = androidTargetCompatibility 61 | } 62 | } 63 | } 64 | plugins.withType(JavaPlugin::class.java) { 65 | extensions.configure(JavaPluginExtension::class.java) { 66 | sourceCompatibility = androidSourceCompatibility 67 | targetCompatibility = androidTargetCompatibility 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /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/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.6.0" 3 | kotlin = "2.0.0" 4 | 5 | [libraries] 6 | cxx = { module = "org.lsposed.libcxx:libcxx", version = "27.0.12077973" } 7 | 8 | [plugins] 9 | agp-app = { id = "com.android.application", version.ref = "agp" } 10 | android-library = { id = "com.android.library", version.ref = "agp" } 11 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 12 | lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version = "1.2" } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LSPosed/Build-var-Spoof/3f67e36c49f56525fe27a5a36ffc80c76c57d2d5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original 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 POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /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 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 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 %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 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 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /module/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /libs 3 | /obj 4 | /release 5 | -------------------------------------------------------------------------------- /module/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import android.databinding.tool.ext.capitalizeUS 2 | import org.apache.tools.ant.filters.FixCrLfFilter 3 | import org.apache.tools.ant.filters.ReplaceTokens 4 | import java.security.MessageDigest 5 | 6 | plugins { 7 | alias(libs.plugins.agp.app) 8 | alias(libs.plugins.lsplugin.cmaker) 9 | } 10 | 11 | val moduleId: String by rootProject.extra 12 | val moduleName: String by rootProject.extra 13 | val verCode: Int by rootProject.extra 14 | val verName: String by rootProject.extra 15 | val commitHash: String by rootProject.extra 16 | val abiList: List by rootProject.extra 17 | val androidMinSdkVersion: Int by rootProject.extra 18 | val author: String by rootProject.extra 19 | val description: String by rootProject.extra 20 | val moduleDescription = description 21 | 22 | android { 23 | defaultConfig { 24 | ndk { 25 | abiFilters.addAll(abiList) 26 | } 27 | } 28 | 29 | buildFeatures { 30 | prefab = true 31 | } 32 | 33 | externalNativeBuild { 34 | cmake { 35 | version = "3.28.0+" 36 | path("src/main/cpp/CMakeLists.txt") 37 | } 38 | } 39 | namespace = "io.github.lsposed.build_var_spoof" 40 | } 41 | 42 | cmaker { 43 | default { 44 | arguments += arrayOf( 45 | "-DANDROID_STL=none", 46 | "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", 47 | "-DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON", 48 | "-DCMAKE_CXX_STANDARD=23", 49 | "-DCMAKE_C_STANDARD=23", 50 | "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON", 51 | "-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON", 52 | "-DCMAKE_CXX_VISIBILITY_PRESET=hidden", 53 | "-DCMAKE_C_VISIBILITY_PRESET=hidden", 54 | ) 55 | abiFilters(*abiList.toTypedArray()) 56 | } 57 | buildTypes { 58 | } 59 | } 60 | 61 | dependencies { 62 | implementation(libs.cxx) 63 | } 64 | 65 | afterEvaluate { 66 | android.applicationVariants.forEach { variant -> 67 | val variantLowered = variant.name.lowercase() 68 | val variantCapped = variant.name.capitalizeUS() 69 | val buildTypeLowered = variant.buildType.name.lowercase() 70 | val supportedAbis = abiList.joinToString(" ") { 71 | when (it) { 72 | "arm64-v8a" -> "arm64" 73 | "armeabi-v7a" -> "arm" 74 | "x86" -> "x86" 75 | "x86_64" -> "x64" 76 | else -> error("unsupported abi $it") 77 | } 78 | } 79 | 80 | val moduleDir = layout.buildDirectory.file("outputs/module/$variantLowered") 81 | val zipFileName = 82 | "$moduleName-$verName-$verCode-$commitHash-$buildTypeLowered.zip".replace(' ', '-') 83 | 84 | val prepareModuleFilesTask = task("prepareModuleFiles$variantCapped") { 85 | group = "module" 86 | dependsOn( 87 | "assemble$variantCapped", 88 | ) 89 | into(moduleDir) 90 | from(rootProject.layout.projectDirectory.file("README.md")) 91 | from(layout.projectDirectory.file("template")) { 92 | exclude("module.prop", "customize.sh") 93 | filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) 94 | } 95 | from(layout.projectDirectory.file("template")) { 96 | include("module.prop") 97 | expand( 98 | "moduleId" to moduleId, 99 | "moduleName" to moduleName, 100 | "versionName" to "$verName ($verCode-$commitHash-$variantLowered)", 101 | "versionCode" to verCode, 102 | "author" to author, 103 | "description" to moduleDescription, 104 | ) 105 | } 106 | from(layout.projectDirectory.file("template")) { 107 | include("customize.sh") 108 | val tokens = mapOf( 109 | "DEBUG" to if (buildTypeLowered == "debug") "true" else "false", 110 | "SONAME" to moduleId, 111 | "SUPPORTED_ABIS" to supportedAbis, 112 | "MIN_SDK" to androidMinSdkVersion.toString() 113 | ) 114 | filter("tokens" to tokens) 115 | filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) 116 | } 117 | from(layout.buildDirectory.file("intermediates/stripped_native_libs/$variantLowered/strip${variantCapped}DebugSymbols/out/lib")) { 118 | into("lib") 119 | } 120 | 121 | doLast { 122 | fileTree(moduleDir).visit { 123 | if (isDirectory) return@visit 124 | val md = MessageDigest.getInstance("SHA-256") 125 | file.forEachBlock(4096) { bytes, size -> 126 | md.update(bytes, 0, size) 127 | } 128 | file(file.path + ".sha256").writeText( 129 | org.apache.commons.codec.binary.Hex.encodeHexString( 130 | md.digest() 131 | ) 132 | ) 133 | } 134 | } 135 | } 136 | 137 | val zipTask = task("zip$variantCapped") { 138 | group = "module" 139 | dependsOn(prepareModuleFilesTask) 140 | archiveFileName.set(zipFileName) 141 | destinationDirectory.set(layout.projectDirectory.file("release").asFile) 142 | from(moduleDir) 143 | } 144 | 145 | val pushTask = task("push$variantCapped") { 146 | group = "module" 147 | dependsOn(zipTask) 148 | commandLine("adb", "push", zipTask.outputs.files.singleFile.path, "/data/local/tmp") 149 | } 150 | 151 | val installKsuTask = task("installKsu$variantCapped") { 152 | group = "module" 153 | dependsOn(pushTask) 154 | commandLine( 155 | "adb", "shell", "su", "-c", 156 | "/data/adb/ksud module install /data/local/tmp/$zipFileName" 157 | ) 158 | } 159 | 160 | val installMagiskTask = task("installMagisk$variantCapped") { 161 | group = "module" 162 | dependsOn(pushTask) 163 | commandLine( 164 | "adb", 165 | "shell", 166 | "su", 167 | "-M", 168 | "-c", 169 | "magisk --install-module /data/local/tmp/$zipFileName" 170 | ) 171 | } 172 | 173 | task("installKsuAndReboot$variantCapped") { 174 | group = "module" 175 | dependsOn(installKsuTask) 176 | commandLine("adb", "reboot") 177 | } 178 | 179 | task("installMagiskAndReboot$variantCapped") { 180 | group = "module" 181 | dependsOn(installMagiskTask) 182 | commandLine("adb", "reboot") 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /module/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | project(trick_store) 3 | 4 | find_package(cxx REQUIRED CONFIG) 5 | link_libraries(cxx::cxx) 6 | 7 | add_library(my_logging STATIC logging/logging.cpp) 8 | 9 | target_include_directories(my_logging PUBLIC logging/include) 10 | 11 | target_link_libraries(my_logging log) 12 | 13 | add_library(zygisk SHARED zygisk/main.cpp) 14 | target_link_libraries(zygisk PRIVATE log my_logging) 15 | -------------------------------------------------------------------------------- /module/src/main/cpp/logging/include/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifndef LOG_TAG 9 | # define LOG_TAG "Build Vars Spoofing" 10 | #endif 11 | 12 | #ifndef NDEBUG 13 | #define LOGD(...) logging::log(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 14 | #define LOGV(...) logging::log(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 15 | #else 16 | #define LOGD(...) (void)0 17 | #define LOGV(...) (void)0 18 | #endif 19 | #define LOGI(...) logging::log(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 20 | #define LOGW(...) logging::log(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 21 | #define LOGE(...) logging::log(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 22 | #define LOGF(...) logging::log(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) 23 | #define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) 24 | 25 | namespace logging { 26 | void setPrintEnabled(bool print); 27 | 28 | [[gnu::format(printf, 3, 4)]] 29 | void log(int prio, const char *tag, const char *fmt, ...); 30 | } 31 | -------------------------------------------------------------------------------- /module/src/main/cpp/logging/logging.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "logging.hpp" 7 | 8 | namespace logging { 9 | static bool use_print = false; 10 | static char prio_str[] = { 11 | 'V', 'D', 'I', 'W', 'E', 'F' 12 | }; 13 | 14 | void setPrintEnabled(bool print) { 15 | use_print = print; 16 | } 17 | 18 | void log(int prio, const char *tag, const char *fmt, ...) { 19 | { 20 | va_list ap; 21 | va_start(ap, fmt); 22 | __android_log_vprint(prio, tag, fmt, ap); 23 | va_end(ap); 24 | } 25 | if (use_print) { 26 | char buf[BUFSIZ]; 27 | va_list ap; 28 | va_start(ap, fmt); 29 | vsnprintf(buf, sizeof(buf), fmt, ap); 30 | va_end(ap); 31 | auto prio_char = (prio > ANDROID_LOG_DEFAULT && prio <= ANDROID_LOG_FATAL) ? prio_str[ 32 | prio - ANDROID_LOG_VERBOSE] : '?'; 33 | printf("[%c][%d:%d][%s]:%s\n", prio_char, getpid(), gettid(), tag, buf); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /module/src/main/cpp/zygisk/main.cpp: -------------------------------------------------------------------------------- 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 "logging.hpp" 14 | #include "zygisk.hpp" 15 | 16 | using zygisk::Api; 17 | using zygisk::AppSpecializeArgs; 18 | using zygisk::ServerSpecializeArgs; 19 | using namespace std::string_view_literals; 20 | 21 | template struct FixedString { 22 | // NOLINTNEXTLINE(*-explicit-constructor) 23 | [[maybe_unused]] consteval inline FixedString(const char (&str)[N]) { 24 | std::copy_n(str, N, data); 25 | } 26 | consteval inline FixedString() = default; 27 | char data[N] = {}; 28 | }; 29 | 30 | using PropValue = std::array; 31 | 32 | template 33 | struct Prop { 34 | using Type [[maybe_unused]] = T; 35 | bool has_value{false}; 36 | PropValue value {}; 37 | 38 | [[maybe_unused]] inline consteval static const char *getField() { 39 | return Field.data; 40 | } 41 | [[maybe_unused]] inline consteval static bool isVersion() { 42 | return Version; 43 | } 44 | }; 45 | 46 | static_assert(sizeof(Prop) % sizeof(void*) == 0); 47 | 48 | using SpoofConfig = std::tuple< 49 | Prop, 50 | Prop, 51 | Prop, 52 | Prop, 53 | Prop, 54 | Prop, 55 | Prop, 56 | Prop, 57 | Prop, 58 | Prop, 59 | Prop, 60 | Prop, 61 | Prop, 62 | Prop, 63 | Prop 64 | >; 65 | 66 | 67 | ssize_t xread(int fd, void *buffer, size_t count) { 68 | ssize_t total = 0; 69 | char *buf = (char *)buffer; 70 | while (count > 0) { 71 | ssize_t ret = read(fd, buf, count); 72 | if (ret < 0) return -1; 73 | buf += ret; 74 | total += ret; 75 | count -= ret; 76 | } 77 | return total; 78 | } 79 | 80 | ssize_t xwrite(int fd, const void *buffer, size_t count) { 81 | ssize_t total = 0; 82 | char *buf = (char *)buffer; 83 | while (count > 0) { 84 | ssize_t ret = write(fd, buf, count); 85 | if (ret < 0) return -1; 86 | buf += ret; 87 | total += ret; 88 | count -= ret; 89 | } 90 | return total; 91 | } 92 | 93 | void trim(std::string_view &str) { 94 | str.remove_prefix(std::min(str.find_first_not_of(" \t"), str.size())); 95 | str.remove_suffix(std::min(str.size() - str.find_last_not_of(" \t") - 1, str.size())); 96 | } 97 | 98 | class Module : public zygisk::ModuleBase { 99 | public: 100 | void onLoad(Api *api, JNIEnv *env) override { 101 | this->api_ = api; 102 | this->env_ = env; 103 | } 104 | 105 | void preAppSpecialize(AppSpecializeArgs *args) override { 106 | api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); 107 | if (args->app_data_dir == nullptr) { 108 | return; 109 | } 110 | 111 | auto app_data_dir = env_->GetStringUTFChars(args->app_data_dir, nullptr); 112 | auto nice_name = env_->GetStringUTFChars(args->nice_name, nullptr); 113 | 114 | std::string_view dir(app_data_dir); 115 | std::string_view process(nice_name); 116 | 117 | bool isGms = false, isGmsUnstable = false; 118 | isGms = dir.ends_with("/com.google.android.gms"); 119 | isGmsUnstable = process == "com.google.android.gms.unstable"; 120 | 121 | env_->ReleaseStringUTFChars(args->app_data_dir, app_data_dir); 122 | env_->ReleaseStringUTFChars(args->nice_name, nice_name); 123 | 124 | if (!isGms) { 125 | return; 126 | } 127 | api_->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); 128 | if (!isGmsUnstable) { 129 | return; 130 | } 131 | 132 | int enabled = 0; 133 | SpoofConfig spoofConfig{}; 134 | auto fd = api_->connectCompanion(); 135 | if (fd >= 0) [[likely]] { 136 | // read enabled 137 | xread(fd, &enabled, sizeof(enabled)); 138 | if (enabled) { 139 | xread(fd, &spoofConfig, sizeof(spoofConfig)); 140 | } 141 | close(fd); 142 | } 143 | if (enabled) { 144 | LOGI("spoofing build vars in GMS!"); 145 | auto buildClass = env_->FindClass("android/os/Build"); 146 | auto buildVersionClass = env_->FindClass("android/os/Build$VERSION"); 147 | 148 | std::apply([this, &buildClass, &buildVersionClass](auto &&... args) { 149 | ((!args.has_value || 150 | (setField::Type>( 151 | std::remove_cvref_t::isVersion() ? buildVersionClass 152 | : buildClass, 153 | std::remove_cvref_t::getField(), 154 | args.value) && 155 | (LOGI("%s set %s to %s", 156 | std::remove_cvref_t::isVersion() ? "VERSION" : "Build", 157 | std::remove_cvref_t::getField(), 158 | args.value.data()), true)) 159 | ? void(0) 160 | : LOGE("%s failed to set %s to %s", 161 | std::remove_cvref_t::isVersion() ? "VERSION" : "Build", 162 | std::remove_cvref_t::getField(), 163 | args.value.data())), ...); 164 | }, spoofConfig); 165 | } 166 | } 167 | 168 | void preServerSpecialize(ServerSpecializeArgs *args) override { 169 | api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); 170 | } 171 | 172 | private: 173 | Api *api_{nullptr}; 174 | JNIEnv *env_{nullptr}; 175 | 176 | template 177 | inline bool setField(jclass clazz, const char* field, const PropValue& value); 178 | 179 | template<> 180 | inline bool setField(jclass clazz, const char* field, const PropValue& value) { 181 | auto id = env_->GetStaticFieldID(clazz, field, "Ljava/lang/String;"); 182 | if (!id) return false; 183 | env_->SetStaticObjectField(clazz, id, env_->NewStringUTF(value.data())); 184 | return true; 185 | } 186 | 187 | template<> 188 | inline bool setField(jclass clazz, const char* field, const PropValue& value) { 189 | auto id = env_->GetStaticFieldID(clazz, field, "I"); 190 | if (!id) return false; 191 | char *p = nullptr; 192 | jint x = static_cast(strtol(value.data(), &p, 10)); 193 | if (p == value.data()) { 194 | return false; 195 | } 196 | env_->SetStaticIntField(clazz, id, x); 197 | return true; 198 | } 199 | 200 | template<> 201 | inline bool setField(jclass clazz, const char* field, const PropValue& value) { 202 | auto id = env_->GetStaticFieldID(clazz, field, "Z"); 203 | if (!id) return false; 204 | auto x = std::string_view(value.data()); 205 | if (x == "1" || x == "true") { 206 | env_->SetStaticBooleanField(clazz, id, JNI_TRUE); 207 | } else if (x == "0" || x == "false") { 208 | env_->SetStaticBooleanField(clazz, id, JNI_FALSE); 209 | } else { 210 | return false; 211 | } 212 | return true; 213 | } 214 | }; 215 | 216 | void read_config(FILE *config, SpoofConfig &spoof_config) { 217 | char *l = nullptr; 218 | struct finally { 219 | char *(&l); 220 | 221 | ~finally() { free(l); } 222 | } finally{l}; 223 | size_t len = 0; 224 | ssize_t n; 225 | while ((n = getline(&l, &len, config)) != -1) { 226 | if (n <= 1) continue; 227 | std::string_view line{l, static_cast(n)}; 228 | if (line.back() == '\n') { 229 | line.remove_suffix(1); 230 | } 231 | auto d = line.find_first_of('='); 232 | if (d == std::string_view::npos) { 233 | LOGW("Ignore invalid line %.*s", static_cast(line.size()), line.data()); 234 | continue; 235 | } 236 | auto key = line.substr(0, d); 237 | trim(key); 238 | auto value = line.substr(d + 1); 239 | trim(value); 240 | std::apply([&key, &value](auto &&... args) { 241 | ((key == std::remove_cvref_t::getField() && 242 | (LOGD("Read config: %.*s = %.*s", static_cast(key.size()), key.data(), 243 | static_cast(value.size()), value.data()), 244 | args.value.size() >= value.size() + 1 ? 245 | (args.has_value = true, 246 | strlcpy(args.value.data(), value.data(), 247 | std::min(args.value.size(), value.size() + 1))) : 248 | (LOGW("Config value %.*s for %.*s is too long, ignored", 249 | static_cast(value.size()), value.data(), 250 | static_cast(key.size()), key.data()), true))) || ...); 251 | }, spoof_config); 252 | } 253 | } 254 | 255 | static void companion_handler(int fd) { 256 | constexpr auto kSpoofConfigFile = "/data/adb/build_var_spoof/spoof_build_vars"sv; 257 | constexpr auto kDefaultSpoofConfig = 258 | R"EOF(MANUFACTURER=Google 259 | MODEL=Pixel 260 | FINGERPRINT=google/sailfish/sailfish:10/QPP3.190404.015/5505587:user/release-keys 261 | BRAND=google 262 | PRODUCT=sailfish 263 | DEVICE=sailfish 264 | RELEASE=10 265 | ID=QPP3.190404.015 266 | INCREMENTAL=5505587 267 | TYPE=user 268 | TAGS=release-keys 269 | SECURITY_PATCH=2019-05-05 270 | )EOF"sv; 271 | struct stat st{}; 272 | int enabled = stat(kSpoofConfigFile.data(), &st) == 0; 273 | xwrite(fd, &enabled, sizeof(enabled)); 274 | 275 | if (!enabled) { 276 | return; 277 | } 278 | 279 | int cfd = -1; 280 | if (st.st_size == 0) { 281 | cfd = open(kSpoofConfigFile.data(), O_RDWR); 282 | if (cfd > 0) { 283 | xwrite(cfd, kDefaultSpoofConfig.data(), kDefaultSpoofConfig.size()); 284 | lseek(cfd, 0, SEEK_SET); 285 | } 286 | } else { 287 | cfd = open(kSpoofConfigFile.data(), O_RDONLY); 288 | } 289 | if (cfd < 0) { 290 | LOGE("[companion_handler] Failed to open spoof_build_vars"); 291 | return; 292 | } 293 | 294 | SpoofConfig spoof_config{}; 295 | std::unique_ptr config{fdopen(cfd, "r")}; 296 | read_config(config.get(), spoof_config); 297 | 298 | xwrite(fd, &spoof_config, sizeof(spoof_config)); 299 | } 300 | 301 | // Register our module class and the companion handler function 302 | REGISTER_ZYGISK_MODULE(Module) 303 | REGISTER_ZYGISK_COMPANION(companion_handler) 304 | -------------------------------------------------------------------------------- /module/src/main/cpp/zygisk/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 | 178 | template 179 | void entry_impl(api_table *, JNIEnv *); 180 | } 181 | 182 | // These values are used in Api::setOption(Option) 183 | enum Option : int { 184 | // Force Magisk's denylist unmount routines to run on this process. 185 | // 186 | // Setting this option only makes sense in preAppSpecialize. 187 | // The actual unmounting happens during app process specialization. 188 | // 189 | // Set this option to force all Magisk and modules' files to be unmounted from the 190 | // mount namespace of the process, regardless of the denylist enforcement status. 191 | FORCE_DENYLIST_UNMOUNT = 0, 192 | 193 | // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. 194 | // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory. 195 | // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. 196 | DLCLOSE_MODULE_LIBRARY = 1, 197 | }; 198 | 199 | // Bit masks of the return value of Api::getFlags() 200 | enum StateFlag : uint32_t { 201 | // The user has granted root access to the current process 202 | PROCESS_GRANTED_ROOT = (1u << 0), 203 | 204 | // The current process was added on the denylist 205 | PROCESS_ON_DENYLIST = (1u << 1), 206 | }; 207 | 208 | // All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded 209 | // from the specialized process afterwards. 210 | struct Api { 211 | 212 | // Connect to a root companion process and get a Unix domain socket for IPC. 213 | // 214 | // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions. 215 | // 216 | // The pre[XXX]Specialize methods run with the same privilege of zygote. 217 | // If you would like to do some operations with superuser permissions, register a handler 218 | // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). 219 | // Another good use case for a companion process is that if you want to share some resources 220 | // across multiple processes, hold the resources in the companion process and pass it over. 221 | // 222 | // The root companion process is ABI aware; that is, when calling this method from a 32-bit 223 | // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. 224 | // 225 | // Returns a file descriptor to a socket that is connected to the socket passed to your 226 | // module's companion request handler. Returns -1 if the connection attempt failed. 227 | int connectCompanion(); 228 | 229 | // Get the file descriptor of the root folder of the current module. 230 | // 231 | // This API only works in the pre[XXX]Specialize methods. 232 | // Accessing the directory returned is only possible in the pre[XXX]Specialize methods 233 | // or in the root companion process (assuming that you sent the fd over the socket). 234 | // Both restrictions are due to SELinux and UID. 235 | // 236 | // Returns -1 if errors occurred. 237 | int getModuleDir(); 238 | 239 | // Set various options for your module. 240 | // Please note that this method accepts one single option at a time. 241 | // Check zygisk::Option for the full list of options available. 242 | void setOption(Option opt); 243 | 244 | // Get information about the current process. 245 | // Returns bitwise-or'd zygisk::StateFlag values. 246 | uint32_t getFlags(); 247 | 248 | // Exempt the provided file descriptor from being automatically closed. 249 | // 250 | // This API only make sense in preAppSpecialize; calling this method in any other situation 251 | // is either a no-op (returns true) or an error (returns false). 252 | // 253 | // When false is returned, the provided file descriptor will eventually be closed by zygote. 254 | bool exemptFd(int fd); 255 | 256 | // Hook JNI native methods for a class 257 | // 258 | // Lookup all registered JNI native methods and replace it with your own methods. 259 | // The original function pointer will be saved in each JNINativeMethod's fnPtr. 260 | // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr 261 | // will be set to nullptr. 262 | void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, 263 | int numMethods); 264 | 265 | // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory. 266 | // 267 | // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example: 268 | // 269 | //
270 | // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64 271 | // (More details: https://man7.org/linux/man-pages/man5/proc.5.html) 272 | // 273 | // The `dev` and `inode` pair uniquely identifies a file being mapped into memory. 274 | // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`. 275 | // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. 276 | void 277 | pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc); 278 | 279 | // Commit all the hooks that was previously registered. 280 | // Returns false if an error occurred. 281 | bool pltHookCommit(); 282 | 283 | private: 284 | internal::api_table *tbl; 285 | 286 | template 287 | friend void internal::entry_impl(internal::api_table *, JNIEnv *); 288 | }; 289 | 290 | // Register a class as a Zygisk module 291 | 292 | #define REGISTER_ZYGISK_MODULE(clazz) \ 293 | void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ 294 | zygisk::internal::entry_impl(table, env); \ 295 | } 296 | 297 | // Register a root companion request handler function for your module 298 | // 299 | // The function runs in a superuser daemon process and handles a root companion request from 300 | // your module running in a target process. The function has to accept an integer value, 301 | // which is a Unix domain socket that is connected to the target process. 302 | // See Api::connectCompanion() for more info. 303 | // 304 | // NOTE: the function can run concurrently on multiple threads. 305 | // Be aware of race conditions if you have globally shared resources. 306 | 307 | #define REGISTER_ZYGISK_COMPANION(func) \ 308 | void zygisk_companion_entry(int client) { func(client); } 309 | 310 | /********************************************************* 311 | * The following is internal ABI implementation detail. 312 | * You do not have to understand what it is doing. 313 | *********************************************************/ 314 | 315 | namespace internal { 316 | 317 | struct module_abi { 318 | long api_version; 319 | ModuleBase *impl; 320 | 321 | void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); 322 | 323 | void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); 324 | 325 | void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); 326 | 327 | void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); 328 | 329 | module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) { 330 | preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); }; 331 | postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); }; 332 | preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); }; 333 | postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); }; 334 | } 335 | }; 336 | 337 | struct api_table { 338 | // Base 339 | void *impl; 340 | 341 | bool (*registerModule)(api_table *, module_abi *); 342 | 343 | void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); 344 | 345 | void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); 346 | 347 | bool (*exemptFd)(int); 348 | 349 | bool (*pltHookCommit)(); 350 | 351 | int (*connectCompanion)(void * /* impl */); 352 | 353 | void (*setOption)(void * /* impl */, Option); 354 | 355 | int (*getModuleDir)(void * /* impl */); 356 | 357 | uint32_t (*getFlags)(void * /* impl */); 358 | }; 359 | 360 | template 361 | void entry_impl(api_table *table, JNIEnv *env) { 362 | static Api api; 363 | api.tbl = table; 364 | static T module; 365 | ModuleBase *m = &module; 366 | static module_abi abi(m); 367 | if (!table->registerModule(table, &abi)) return; 368 | m->onLoad(&api, env); 369 | } 370 | 371 | } // namespace internal 372 | 373 | inline int Api::connectCompanion() { 374 | return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1; 375 | } 376 | 377 | inline int Api::getModuleDir() { 378 | return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1; 379 | } 380 | 381 | inline void Api::setOption(Option opt) { 382 | if (tbl->setOption) tbl->setOption(tbl->impl, opt); 383 | } 384 | 385 | inline uint32_t Api::getFlags() { 386 | return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; 387 | } 388 | 389 | inline bool Api::exemptFd(int fd) { 390 | return tbl->exemptFd != nullptr && tbl->exemptFd(fd); 391 | } 392 | 393 | inline void 394 | Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, 395 | int numMethods) { 396 | if (tbl->hookJniNativeMethods) 397 | tbl->hookJniNativeMethods(env, className, methods, numMethods); 398 | } 399 | 400 | inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, 401 | void **oldFunc) { 402 | if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc); 403 | } 404 | 405 | inline bool Api::pltHookCommit() { 406 | return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); 407 | } 408 | 409 | } // namespace zygisk 410 | 411 | extern "C" { 412 | 413 | [[gnu::visibility("default"), maybe_unused]] 414 | void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); 415 | 416 | [[gnu::visibility("default"), maybe_unused]] 417 | void zygisk_companion_entry(int); 418 | 419 | } // extern "C" 420 | -------------------------------------------------------------------------------- /module/template/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 34 | -------------------------------------------------------------------------------- /module/template/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /module/template/customize.sh: -------------------------------------------------------------------------------- 1 | # shellcheck disable=SC2034 2 | SKIPUNZIP=1 3 | 4 | DEBUG=@DEBUG@ 5 | SONAME=@SONAME@ 6 | SUPPORTED_ABIS="@SUPPORTED_ABIS@" 7 | MIN_SDK=@MIN_SDK@ 8 | 9 | if [ "$BOOTMODE" ] && [ "$KSU" ]; then 10 | ui_print "- Installing from KernelSU app" 11 | ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)" 12 | if [ "$(which magisk)" ]; then 13 | ui_print "*********************************************************" 14 | ui_print "! Multiple root implementation is NOT supported!" 15 | ui_print "! Please uninstall Magisk before installing Zygisk Next" 16 | abort "*********************************************************" 17 | fi 18 | elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then 19 | ui_print "- Installing from Magisk app" 20 | else 21 | ui_print "*********************************************************" 22 | ui_print "! Install from recovery is not supported" 23 | ui_print "! Please install from KernelSU or Magisk app" 24 | abort "*********************************************************" 25 | fi 26 | 27 | VERSION=$(grep_prop version "${TMPDIR}/module.prop") 28 | ui_print "- Installing $SONAME $VERSION" 29 | 30 | # check architecture 31 | support=false 32 | for abi in $SUPPORTED_ABIS 33 | do 34 | if [ "$ARCH" == "$abi" ]; then 35 | support=true 36 | fi 37 | done 38 | if [ "$support" == "false" ]; then 39 | abort "! Unsupported platform: $ARCH" 40 | else 41 | ui_print "- Device platform: $ARCH" 42 | fi 43 | 44 | # check android 45 | if [ "$API" -lt $MIN_SDK ]; then 46 | ui_print "! Unsupported sdk: $API" 47 | abort "! Minimal supported sdk is $MIN_SDK" 48 | else 49 | ui_print "- Device sdk: $API" 50 | fi 51 | 52 | ui_print "- Extracting verify.sh" 53 | unzip -o "$ZIPFILE" 'verify.sh' -d "$TMPDIR" >&2 54 | if [ ! -f "$TMPDIR/verify.sh" ]; then 55 | ui_print "*********************************************************" 56 | ui_print "! Unable to extract verify.sh!" 57 | ui_print "! This zip may be corrupted, please try downloading again" 58 | abort "*********************************************************" 59 | fi 60 | . "$TMPDIR/verify.sh" 61 | extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip" 62 | extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip" 63 | 64 | ui_print "- Extracting module files" 65 | extract "$ZIPFILE" 'module.prop' "$MODPATH" 66 | 67 | mkdir "$MODPATH/zygisk" 68 | 69 | if [ "$ARCH" = "arm64" ]; then 70 | ui_print "- Extracting arm64 libraries" 71 | extract "$ZIPFILE" "lib/arm64-v8a/libzygisk.so" "$MODPATH/zygisk" true 72 | mv "$MODPATH/zygisk/libzygisk.so" "$MODPATH/zygisk/arm64-v8a.so" 73 | elif [ "$ARCH" = "x64" ]; then 74 | ui_print "- Extracting x64 libraries" 75 | extract "$ZIPFILE" "lib/x86_64/libzygisk.so" "$MODPATH/zygisk" true 76 | mv "$MODPATH/zygisk/libzygisk.so" "$MODPATH/zygisk/x86_64.so" 77 | elif [ "$ARCH" = "arm" ]; then 78 | ui_print "- Extracting arm libraries" 79 | extract "$ZIPFILE" "lib/armeabi-v7a/libzygisk.so" "$MODPATH/zygisk" true 80 | mv "$MODPATH/zygisk/libzygisk.so" "$MODPATH/zygisk/armeabi-v7a.so" 81 | elif [ "$ARCH" = "x86" ]; then 82 | ui_print "- Extracting x86 libraries" 83 | extract "$ZIPFILE" "lib/x86/libzygisk.so" "$MODPATH/zygisk" true 84 | mv "$MODPATH/zygisk/libzygisk.so" "$MODPATH/zygisk/x86.so" 85 | fi 86 | 87 | CONFIG_DIR=/data/adb/build_var_spoof 88 | if [ ! -d "$CONFIG_DIR" ]; then 89 | ui_print "- Creating configuration directory" 90 | mkdir -p "$CONFIG_DIR" 91 | [ ! -f "$CONFIG_DIR/spoof_build_vars" ] && touch "$CONFIG_DIR/spoof_build_vars" 92 | fi 93 | -------------------------------------------------------------------------------- /module/template/module.prop: -------------------------------------------------------------------------------- 1 | id=${moduleId} 2 | name=${moduleName} 3 | version=${versionName} 4 | versionCode=${versionCode} 5 | author=${author} 6 | description=${description} 7 | #updateJson= 8 | -------------------------------------------------------------------------------- /module/template/verify.sh: -------------------------------------------------------------------------------- 1 | TMPDIR_FOR_VERIFY="$TMPDIR/.vunzip" 2 | mkdir "$TMPDIR_FOR_VERIFY" 3 | 4 | abort_verify() { 5 | ui_print "*********************************************************" 6 | ui_print "! $1" 7 | ui_print "! This zip may be corrupted, please try downloading again" 8 | abort "*********************************************************" 9 | } 10 | 11 | # extract 12 | extract() { 13 | zip=$1 14 | file=$2 15 | dir=$3 16 | junk_paths=$4 17 | [ -z "$junk_paths" ] && junk_paths=false 18 | opts="-o" 19 | [ "$junk_paths" = true ] && opts="-oj" 20 | 21 | file_path="" 22 | hash_path="" 23 | if [ "$junk_paths" = true ]; then 24 | file_path="$dir/$(basename "$file")" 25 | hash_path="$TMPDIR_FOR_VERIFY/$(basename "$file").sha256" 26 | else 27 | file_path="$dir/$file" 28 | hash_path="$TMPDIR_FOR_VERIFY/$file.sha256" 29 | fi 30 | 31 | unzip $opts "$zip" "$file" -d "$dir" >&2 32 | [ -f "$file_path" ] || abort_verify "$file not exists" 33 | 34 | unzip $opts "$zip" "$file.sha256" -d "$TMPDIR_FOR_VERIFY" >&2 35 | [ -f "$hash_path" ] || abort_verify "$file.sha256 not exists" 36 | 37 | (echo "$(cat "$hash_path") $file_path" | sha256sum -c -s -) || abort_verify "Failed to verify $file" 38 | ui_print "- Verified $file" >&1 39 | } 40 | 41 | file="META-INF/com/google/android/update-binary" 42 | file_path="$TMPDIR_FOR_VERIFY/$file" 43 | hash_path="$file_path.sha256" 44 | unzip -o "$ZIPFILE" "META-INF/com/google/android/*" -d "$TMPDIR_FOR_VERIFY" >&2 45 | [ -f "$file_path" ] || abort_verify "$file not exists" 46 | if [ -f "$hash_path" ]; then 47 | (echo "$(cat "$hash_path") $file_path" | sha256sum -c -s -) || abort_verify "Failed to verify $file" 48 | ui_print "- Verified $file" >&1 49 | else 50 | ui_print "- Download from Magisk app" 51 | fi 52 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | } 15 | } 16 | 17 | rootProject.name = "Build Vars Spoofing" 18 | include(":module") 19 | --------------------------------------------------------------------------------