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