diff --git a/app/src/main/cpp/module.cpp b/app/src/main/cpp/module.cpp index a6c7a2b..1aba2db 100644 --- a/app/src/main/cpp/module.cpp +++ b/app/src/main/cpp/module.cpp @@ -12,13 +12,11 @@ static std::string SECURITY_PATCH, FIRST_API_LEVEL, VNDK_VERSION, BUILD_ID; typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); -static std::map callbacks; +static volatile T_Callback o_callback = nullptr; static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { - if (cookie == nullptr || name == nullptr || value == nullptr || - !callbacks.contains(cookie)) - return; + if (cookie == nullptr || name == nullptr || value == nullptr || o_callback == nullptr) return; std::string_view prop(name); @@ -26,25 +24,23 @@ static void modify_callback(void *cookie, const char *name, const char *value, u if (!SECURITY_PATCH.empty()) { value = SECURITY_PATCH.c_str(); } - LOGD("[%s]: %s", name, value); } else if (prop.ends_with("api_level")) { if (!FIRST_API_LEVEL.empty()) { value = FIRST_API_LEVEL.c_str(); } - LOGD("[%s]: %s", name, value); } else if (prop.ends_with("vndk.version")) { if (!VNDK_VERSION.empty()) { value = VNDK_VERSION.c_str(); } - LOGD("[%s]: %s", name, value); } else if (prop == "ro.build.id") { if (!BUILD_ID.empty()) { value = BUILD_ID.c_str(); } - LOGD("[%s]: %s", name, value); } - return callbacks[cookie](cookie, name, value, serial); + if (!prop.starts_with("cache") && !prop.starts_with("debug")) LOGD("[%s]: %s", name, value); + + return o_callback(cookie, name, value, serial); } static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *); @@ -54,7 +50,7 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void if (pi == nullptr || callback == nullptr || cookie == nullptr) { return o_system_property_read_callback(pi, callback, cookie); } - callbacks[cookie] = callback; + o_callback = callback; return o_system_property_read_callback(pi, modify_callback, cookie); } @@ -81,37 +77,41 @@ public: void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr); - std::string process(rawProcess); - env->ReleaseStringUTFChars(args->nice_name, rawProcess); + auto name = env->GetStringUTFChars(args->nice_name, nullptr); - if (process.starts_with("com.google.android.gms")) { + if (name && strncmp(name, "com.google.android.gms", 22) == 0) { api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - if (process == "com.google.android.gms.unstable") { + if (strcmp(name, "com.google.android.gms.unstable") == 0) { int fd = api->connectCompanion(); read(fd, &dexSize, sizeof(dexSize)); read(fd, &jsonSize, sizeof(jsonSize)); - auto vectorSize = dexSize + jsonSize; + if (dexSize < 1 || jsonSize < 1) { - if (vectorSize > 0) { - vector.resize(vectorSize); - read(fd, vector.data(), vectorSize); - } else { - LOGD("Couldn't read classes.dex"); + LOGD("Couldn't read files in memory!"); api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + + } else { + + auto vectorSize = dexSize + jsonSize; + + if (vectorSize > 0) { + vector.resize(vectorSize); + read(fd, vector.data(), vectorSize); + } } close(fd); - return; - } - } - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + } else api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + + } else api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + + env->ReleaseStringUTFChars(args->nice_name, name); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { @@ -148,7 +148,6 @@ private: if (json.contains("SECURITY_PATCH")) { if (json["SECURITY_PATCH"].is_null() || json["SECURITY_PATCH"].empty()) { LOGD("SECURITY_PATCH is null or empty"); - json.erase("SECURITY_PATCH"); } else { SECURITY_PATCH = json["SECURITY_PATCH"].get(); } @@ -230,10 +229,7 @@ static void companion(int fd) { FILE *json = fopen("/data/adb/pif.json", "rb"); - if (json == nullptr) { - - json = fopen("/data/adb/modules/playintegrityfix/pif.json", "rb"); - } + if (json == nullptr) json = fopen("/data/adb/modules/playintegrityfix/pif.json", "rb"); if (json) { fseek(json, 0, SEEK_END); 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..b51fafc --- /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; + +public class CustomKeyStoreSpi extends KeyStoreSpi { + protected 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) { + + if (EntryPoint.isDroidGuard()) { + + EntryPoint.LOG("DroidGuard call certificate chain! Throwing 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 fd0f531..747a9ca 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -1,30 +1,25 @@ package es.chiteroman.playintegrityfix; import java.security.Provider; -import java.security.ProviderException; -import java.util.Arrays; -import java.util.Locale; -public final class CustomProvider extends Provider { +public class CustomProvider extends Provider { - public CustomProvider(Provider provider) { + protected CustomProvider(Provider provider) { super(provider.getName(), provider.getVersion(), provider.getInfo()); + putAll(provider); + + remove("KeyStore.AndroidKeyStore"); + + put("KeyStore.AndroidKeyStore", CustomKeyStoreSpi.class.getName()); } @Override public synchronized Service getService(String type, String algorithm) { + EntryPoint.LOG("[SERVICE] Type: " + type + " | Algorithm: " + algorithm); + EntryPoint.spoofDevice(); - if ("KeyPairGenerator".equals(type)) { - - if (Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.US).contains("droidguard"))) { - - throw new ProviderException(); - - } - } - 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 13f1566..4b2ef80 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -4,16 +4,18 @@ import android.os.Build; import android.util.JsonReader; import android.util.Log; -import org.json.JSONException; - import java.io.StringReader; import java.lang.reflect.Field; +import java.security.KeyStore; +import java.security.KeyStoreSpi; import java.security.Provider; import java.security.Security; +import java.util.Arrays; import java.util.HashMap; +import java.util.Locale; import java.util.Map; -public final class EntryPoint { +public class EntryPoint { private static final Map map = new HashMap<>(); public static void init(String data) { @@ -22,32 +24,41 @@ public final class EntryPoint { while (reader.hasNext()) { String key = reader.nextName(); String value = reader.nextString(); - - if (key == null || key.isEmpty() || key.isBlank() || value == null || value.isEmpty() || value.isBlank()) - throw new JSONException("Empty key or value"); - map.put(key, value); } reader.endObject(); } catch (Exception e) { LOG("Couldn't parse JSON from Zygisk lib: " + e); - LOG("Remove /data/adb/pif.json"); } + LOG("Map info (keys and values):"); + map.forEach((s, s2) -> LOG(String.format("[%s] -> %s", s, s2))); + spoofDevice(); spoofProvider(); } - static void LOG(String msg) { + protected static void LOG(String msg) { Log.d("PIF/Java", msg); } - static void spoofDevice() { + protected static void spoofDevice() { map.forEach(EntryPoint::setFieldValue); } + protected static boolean isDroidGuard() { + return Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.ENGLISH).contains("droidguard")); + } + private static void spoofProvider() { try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + + Field field = keyStore.getClass().getDeclaredField("keyStoreSpi"); + + field.setAccessible(true); + CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) field.get(keyStore); + Provider provider = Security.getProvider("AndroidKeyStore"); Provider customProvider = new CustomProvider(provider); @@ -79,12 +90,11 @@ public final class EntryPoint { String oldValue = null; try { oldValue = (String) field.get(null); + if (value.equals(oldValue)) return; field.set(null, value); } catch (IllegalAccessException e) { LOG("Couldn't get or set field: " + e); } - field.setAccessible(false); - if (value.equals(oldValue)) return; - LOG(String.format("[%s]: %s -> %s", name, oldValue, value)); + LOG(String.format("Field '%s' with value '%s' is now set to '%s'", name, oldValue, value)); } } \ No newline at end of file diff --git a/changelog.md b/changelog.md index b3142a4..591122a 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,6 @@ We have a Telegram channel! If you want to share your knowledge join: https://t.me/playintegrityfix -# v14.5 +# v14.6 -Fix Google update \ No newline at end of file +Updated fingerprint \ No newline at end of file diff --git a/module/customize.sh b/module/customize.sh index c6be2dd..830de75 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -6,12 +6,13 @@ fi # safetynet-fix module is obsolete and it's incompatible with PIF. if [ -d /data/adb/modules/safetynet-fix ]; then rm -rf /data/adb/modules/safetynet-fix - ui_print "- ! safetynet-fix module will be removed. Do NOT install it again along PIF." + rm -f /data/adb/SNFix.dex + ui_print "! safetynet-fix module will be removed. Do NOT install it again along PIF." fi # MagiskHidePropsConf module is obsolete in Android 8+ but it shouldn't give issues. if [ -d /data/adb/modules/MagiskHidePropsConf ]; then - ui_print "- ! WARNING, MagiskHidePropsConf module may cause issues with PIF." + ui_print "! WARNING, MagiskHidePropsConf module may cause issues with PIF." fi # Remove xiaomi.eu apps @@ -29,6 +30,17 @@ fi # Remove EliteRoms app +if [ -d "/system/app/XInjectModule" ]; then + + directory="$MODPATH/system/app/XInjectModule" + + [ -d "$directory" ] || mkdir -p "$directory" + + touch "$directory/.replace" + + ui_print "- XInjectModule app removed." +fi + if [ -d "/system/app/EliteDevelopmentModule" ]; then directory="$MODPATH/system/app/EliteDevelopmentModule" @@ -39,3 +51,9 @@ if [ -d "/system/app/EliteDevelopmentModule" ]; then ui_print "- EliteDevelopmentModule app removed." fi + +ui_print "! DO NOT REMOVE /data/adb/modules/playintegrityfix/pif.json" +ui_print "! THIS FILE CONTAINS THE DEFAULT PROPS TO SPOOF A CERTIFIED DEVICE" +ui_print "! IF YOU WANT TO USE YOUR CUSTOM PROPS, COPY THIS FILE TO /data/adb/pif.json" +ui_print "! MODULE WILL READ FIRST THAT FILE, IF IT ISN'T EXIST, IT WILL READ THE MODULE FOLDER ONE" +ui_print "! IF YOU REMOVE BOTH FILES EXPECT CRASHES" \ No newline at end of file diff --git a/module/module.prop b/module/module.prop index d9d9fdb..1a1ebda 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=playintegrityfix name=Play Integrity Fix -version=v14.5 -versionCode=14500 +version=v14.6 +versionCode=14600 author=chiteroman description=Fuck Play Integrity API. updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json \ No newline at end of file diff --git a/module/pif.json b/module/pif.json index a97a48b..1e1927a 100644 --- a/module/pif.json +++ b/module/pif.json @@ -1,9 +1,12 @@ { - "PRODUCT": "b1-780_ww_gen1", - "DEVICE": "acer_barricadewifi", - "MANUFACTURER": "Acer", + "PRODUCT": "c01_ww", + "DEVICE": "acer_c01", + "MANUFACTURER": "Acer Inc.", "BRAND": "acer", - "MODEL": "B1-780", - "FINGERPRINT": "acer/b1-780_ww_gen1/acer_barricadewifi:6.0/MRA58K/1481784106:user/release-keys", - "FIRST_API_LEVEL": "21" + "MODEL": "C01", + "FINGERPRINT": "acer/c01_ww/acer_c01:7.1.1/NMF26F/1521514970:user/release-keys", + "FIRST_API_LEVEL": "24", + "SECURITY_PATCH": "2018-04-01", + "VNDK_VERSION": null, + "BUILD_ID": null } diff --git a/update.json b/update.json index 63849bf..d8f31f0 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v14.5", - "versionCode": 14500, - "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v14.5/PlayIntegrityFix_v14.5.zip", + "version": "v14.6", + "versionCode": 14600, + "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v14.6/PlayIntegrityFix_v14.6.zip", "changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md" } \ No newline at end of file