diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9661ac7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4b64b1e..a6663bb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "es.chiteroman.playintegrityfix" minSdk = 26 targetSdk = 34 - versionCode = 15700 - versionName = "v15.7" + versionCode = 15701 + versionName = "v15.7.1" multiDexEnabled = false buildFeatures { diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 83361da..836cd34 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -200,6 +200,68 @@ private: } void parseJson() { + if (json.contains("FIRST_API_LEVEL")) { + + if (json["FIRST_API_LEVEL"].is_number_integer()) { + + FIRST_API_LEVEL = std::to_string(json["FIRST_API_LEVEL"].get()); + + } else if (json["FIRST_API_LEVEL"].is_string()) { + + FIRST_API_LEVEL = json["FIRST_API_LEVEL"].get(); + } + + json.erase("FIRST_API_LEVEL"); + + } else if (json.contains("DEVICE_INITIAL_SDK_INT")) { + + if (json["DEVICE_INITIAL_SDK_INT"].is_number_integer()) { + + FIRST_API_LEVEL = std::to_string(json["DEVICE_INITIAL_SDK_INT"].get()); + + } else if (json["DEVICE_INITIAL_SDK_INT"].is_string()) { + + FIRST_API_LEVEL = json["DEVICE_INITIAL_SDK_INT"].get(); + } + + } else { + + LOGD("JSON file doesn't contain FIRST_API_LEVEL or DEVICE_INITIAL_SDK_INT keys :("); + } + + if (json.contains("SECURITY_PATCH")) { + + if (json["SECURITY_PATCH"].is_string()) { + + SECURITY_PATCH = json["SECURITY_PATCH"].get(); + } + + } else { + + LOGD("JSON file doesn't contain SECURITY_PATCH key :("); + } + + if (json.contains("ID")) { + + if (json["ID"].is_string()) { + + BUILD_ID = json["ID"].get(); + } + + } else if (json.contains("BUILD_ID")) { + + if (json["BUILD_ID"].is_string()) { + + BUILD_ID = json["BUILD_ID"].get(); + } + + json["ID"] = BUILD_ID; + json.erase("BUILD_ID"); + + } else { + + LOGD("JSON file doesn't contain ID/BUILD_ID keys :("); + } } }; diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java new file mode 100644 index 0000000..72ec9a3 --- /dev/null +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java @@ -0,0 +1,105 @@ +package es.chiteroman.playintegrityfix; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.KeyStoreException; +import java.security.KeyStoreSpi; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Enumeration; +import java.util.Locale; + +public final class CustomKeyStoreSpi extends KeyStoreSpi { + public static volatile KeyStoreSpi keyStoreSpi; + + @Override + public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { + return keyStoreSpi.engineGetKey(alias, password); + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) { + if (stackTraceElement.getClassName().toLowerCase(Locale.US).contains("droidguard")) { + EntryPoint.LOG("DroidGuard call certificate chain! Throw exception."); + throw new UnsupportedOperationException(); + } + } + return keyStoreSpi.engineGetCertificateChain(alias); + } + + @Override + public Certificate engineGetCertificate(String alias) { + return keyStoreSpi.engineGetCertificate(alias); + } + + @Override + public Date engineGetCreationDate(String alias) { + return keyStoreSpi.engineGetCreationDate(alias); + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException { + keyStoreSpi.engineSetKeyEntry(alias, key, password, chain); + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { + keyStoreSpi.engineSetKeyEntry(alias, key, chain); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { + keyStoreSpi.engineSetCertificateEntry(alias, cert); + } + + @Override + public void engineDeleteEntry(String alias) throws KeyStoreException { + keyStoreSpi.engineDeleteEntry(alias); + } + + @Override + public Enumeration engineAliases() { + return keyStoreSpi.engineAliases(); + } + + @Override + public boolean engineContainsAlias(String alias) { + return keyStoreSpi.engineContainsAlias(alias); + } + + @Override + public int engineSize() { + return keyStoreSpi.engineSize(); + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return keyStoreSpi.engineIsKeyEntry(alias); + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + return keyStoreSpi.engineIsCertificateEntry(alias); + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + return keyStoreSpi.engineGetCertificateAlias(cert); + } + + @Override + public void engineStore(OutputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException { + keyStoreSpi.engineStore(stream, password); + } + + @Override + public void engineLoad(InputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException { + keyStoreSpi.engineLoad(stream, password); + } +} diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java index cadd407..e577a91 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -1,7 +1,6 @@ package es.chiteroman.playintegrityfix; import java.security.Provider; -import java.security.ProviderException; public final class CustomProvider extends Provider { @@ -9,19 +8,15 @@ public final class CustomProvider extends Provider { super(provider.getName(), provider.getVersion(), provider.getInfo()); putAll(provider); + + put("KeyStore.AndroidKeyStore", CustomKeyStoreSpi.class.getName()); } @Override public synchronized Service getService(String type, String algorithm) { - EntryPoint.spoofFields(); - EntryPoint.LOG(String.format("Service: '%s' | Algorithm: '%s'", type, algorithm)); - if ("AndroidKeyStore".equals(algorithm)) { - Service service = super.getService(type, algorithm); - EntryPoint.LOG(service.toString()); - throw new ProviderException(); - } + EntryPoint.spoofFields(); return super.getService(type, algorithm); } diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index 41c7add..ceff71e 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -6,6 +6,8 @@ import android.util.Log; import org.json.JSONObject; import java.lang.reflect.Field; +import java.security.KeyStore; +import java.security.KeyStoreSpi; import java.security.Provider; import java.security.Security; import java.util.HashMap; @@ -15,6 +17,17 @@ public final class EntryPoint { private static final Map map = new HashMap<>(); static { + try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + + Field field = keyStore.getClass().getDeclaredField("keyStoreSpi"); + field.setAccessible(true); + + CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) field.get(keyStore); + } catch (Throwable t) { + LOG("Error spoofing AndroidKeyStore: " + t); + } + Provider provider = Security.getProvider("AndroidKeyStore"); Provider customProvider = new CustomProvider(provider); diff --git a/changelog.md b/changelog.md index 43af10d..0104f25 100644 --- a/changelog.md +++ b/changelog.md @@ -2,12 +2,17 @@ We have a Telegram group! If you want to share your knowledge join: https://t.me/playintegrityfix -# v15.6 - -- Fix bootloop issue in modern devices. -- Move code logic to C. -- Minor improvements. - Device verdict should pass by default. If not, try removing /data/adb/pif.json file. -DO NOT REMOVE pif.json in module's folder! \ No newline at end of file +DO NOT REMOVE pif.json in module's folder! + +# v15.7.1 + +- Fix crash issue when JSON file have comments. +- Fix hooking in older Android versions. +- Fix CTS profile / Device verdict failures in few devices due bad spoofing code. +- Fix spoofing Provider issue. +- Added post-fs-data.sh script. +- Using latest (compileable) version of Dobby. +- Using RikkaW libcxx prefab. +- Update Gradle. \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49..ccebba7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f71f90f..7ea5fa1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,9 +1,7 @@ -#Wed Feb 07 09:57:46 CET 2024 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +#Wed Feb 07 18:13:12 CET 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1aa94a4..79a61d4 100755 --- a/gradlew +++ b/gradlew @@ -83,8 +83,10 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -131,13 +133,10 @@ location of your Java installation." fi else JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. @@ -145,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,15 +197,11 @@ if "$cygwin" || "$msys" ; then done fi - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/module/module.prop b/module/module.prop index d8df682..4d5301e 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=playintegrityfix name=Play Integrity Fix -version=v15.7 -versionCode=15700 +version=v15.7.1 +versionCode=15701 author=chiteroman description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8+. updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json diff --git a/update.json b/update.json index b980911..ce245b2 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v15.6", - "versionCode": 15600, - "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.6/PlayIntegrityFix_v15.6.zip", + "version": "v15.7.1", + "versionCode": 15701, + "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.7/PlayIntegrityFix_v15.7.1.zip", "changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md" } \ No newline at end of file