diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b589d56..8fabff5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 0c0c338..9a986ae 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index cb865f6..dd62d95 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,19 +1,18 @@ - - - - - - + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml index f8051a6..48052b2 100644 --- a/.idea/migrations.xml +++ b/.idea/migrations.xml @@ -1,10 +1,10 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..5f2840b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,9 +1,10 @@ - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index bfce638..c8397c9 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,7 +1,6 @@ - - - - - - + + + + + \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 94f3289..2f11af0 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,3 @@ --ignorewarnings --dontobfuscate -keep class es.chiteroman.playintegrityfix.EntryPoint {public ;} -keep class es.chiteroman.playintegrityfix.CustomProvider --keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi --keep class es.chiteroman.playintegrityfix.CustomCertificates \ No newline at end of file +-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi \ No newline at end of file diff --git a/app/src/main/cpp/module.cpp b/app/src/main/cpp/module.cpp index b7e6254..0d0d559 100644 --- a/app/src/main/cpp/module.cpp +++ b/app/src/main/cpp/module.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "zygisk.hpp" #include "dobby.h" @@ -48,6 +47,10 @@ static void modify_callback(void *cookie, const char *name, const char *value, u value = BUILD_ID.c_str(); LOGD("Set '%s' to '%s'", name, value); } + + } else if (prop == "sys.usb.state") { + + value = "none"; } return o_callback(cookie, name, value, serial); @@ -87,6 +90,11 @@ public: void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { + if (args->is_child_zygote && *args->is_child_zygote) { + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + return; + } + auto name = env->GetStringUTFChars(args->nice_name, nullptr); if (name && strncmp(name, "com.google.android.gms", 22) == 0) { @@ -95,24 +103,39 @@ public: if (strcmp(name, "com.google.android.gms.unstable") == 0) { - auto rawDir = env->GetStringUTFChars(args->app_data_dir, nullptr); - dir = rawDir; - env->ReleaseStringUTFChars(args->app_data_dir, rawDir); - - long size = dir.size(); - bool done = false; - int fd = api->connectCompanion(); - write(fd, &size, sizeof(long)); - write(fd, dir.data(), size); + read(fd, &dexSize, sizeof(long)); - read(fd, &done, sizeof(bool)); + if (dexSize > 0) { + dexBuffer = new unsigned char[dexSize]; + read(fd, dexBuffer, dexSize); + + } else { + + LOGD("Couldn't load classes.dex file in memory!"); + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + goto end; + } + + read(fd, &jsonSize, sizeof(long)); + + if (jsonSize > 0) { + + jsonBuffer = new char[jsonSize]; + read(fd, jsonBuffer, jsonSize); + + } else { + + LOGD("Couldn't load pif.json file in memory!"); + delete[] dexBuffer; + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + goto end; + } + + end: close(fd); - - LOGD("Files copied: %d", done); - goto clear; } } @@ -124,31 +147,10 @@ public: } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (dir.empty()) return; + if (dexSize < 1 || jsonSize < 1 || dexBuffer == nullptr || jsonBuffer == nullptr) return; - std::string classesDex(dir + "/classes.dex"); - - FILE *dexFile = fopen(classesDex.c_str(), "rb"); - - if (dexFile == nullptr) { - - LOGD("classes.dex doesn't exist... This is weird."); - dir.clear(); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - return; - } - - fclose(dexFile); - - doHook(); - - std::string pifJson(dir + "/pif.json"); - - FILE *jsonFile = fopen(pifJson.c_str(), "r"); - - nlohmann::json json = nlohmann::json::parse(jsonFile, nullptr, false, true); - - fclose(jsonFile); + std::string_view jsonStr(jsonBuffer, jsonSize); + nlohmann::json json = nlohmann::json::parse(jsonStr, nullptr, false, true); if (json.contains("FIRST_API_LEVEL")) { @@ -180,20 +182,20 @@ public: LOGD("JSON file doesn't contain SECURITY_PATCH key :("); } - if (json.contains("BUILD_ID")) { + if (json.contains("ID")) { - if (json["BUILD_ID"].is_string()) { + if (json["ID"].is_string()) { - BUILD_ID = json["BUILD_ID"].get(); + BUILD_ID = json["ID"].get(); } - json.erase("BUILD_ID"); - } else { LOGD("JSON file doesn't contain BUILD_ID key :("); } + doHook(); + LOGD("get system classloader"); auto clClass = env->FindClass("java/lang/ClassLoader"); auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", @@ -201,11 +203,11 @@ public: auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); LOGD("create class loader"); - auto dexClClass = env->FindClass("dalvik/system/PathClassLoader"); + auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClInit = env->GetMethodID(dexClClass, "", - "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"); - auto dexStr = env->NewStringUTF(classesDex.c_str()); - auto dexCl = env->NewObject(dexClClass, dexClInit, dexStr, systemClassLoader); + "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + auto buffer = env->NewDirectByteBuffer(dexBuffer, dexSize); + auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); LOGD("load class"); auto loadClass = env->GetMethodID(clClass, "loadClass", @@ -220,7 +222,16 @@ public: auto str = env->NewStringUTF(json.dump().c_str()); env->CallStaticVoidMethod(entryClass, entryInit, str); - dir.clear(); + env->DeleteLocalRef(clClass); + env->DeleteLocalRef(dexClClass); + env->DeleteLocalRef(buffer); + env->DeleteLocalRef(dexCl); + env->DeleteLocalRef(entryClassName); + env->DeleteLocalRef(entryClassObj); + env->DeleteLocalRef(str); + + delete[] dexBuffer; + delete[] jsonBuffer; } void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { @@ -230,42 +241,53 @@ public: private: zygisk::Api *api = nullptr; JNIEnv *env = nullptr; - std::string dir; + long dexSize = 0, jsonSize = 0; + unsigned char *dexBuffer = nullptr; + char *jsonBuffer = nullptr; }; static void companion(int fd) { + long dexSize = 0, jsonSize = 0; + unsigned char *dexBuffer = nullptr; + char *jsonBuffer = nullptr; - long size = 0; - std::string dir; + FILE *dexFile = fopen(CLASSES_DEX, "rb"); - read(fd, &size, sizeof(long)); + if (dexFile) { - dir.resize(size); + fseek(dexFile, 0, SEEK_END); + dexSize = ftell(dexFile); + fseek(dexFile, 0, SEEK_SET); - read(fd, dir.data(), size); + dexBuffer = new unsigned char[dexSize]; + fread(dexBuffer, 1, dexSize, dexFile); - LOGD("[ROOT] GMS dir: %s", dir.c_str()); + fclose(dexFile); + } - std::string classesDex(dir + "/classes.dex"); - std::string pifJson(dir + "/pif.json"); + write(fd, &dexSize, sizeof(long)); + write(fd, dexBuffer, dexSize); - bool a = std::filesystem::copy_file(CLASSES_DEX, classesDex, - std::filesystem::copy_options::overwrite_existing); + delete[] dexBuffer; - std::filesystem::permissions(classesDex, std::filesystem::perms::owner_read | - std::filesystem::perms::group_read | - std::filesystem::perms::others_read); + FILE *jsonFile = fopen(PIF_JSON, "r"); - bool b = std::filesystem::copy_file(PIF_JSON, pifJson, - std::filesystem::copy_options::overwrite_existing); + if (jsonFile) { - std::filesystem::permissions(pifJson, std::filesystem::perms::owner_read | - std::filesystem::perms::group_read | - std::filesystem::perms::others_read); + fseek(jsonFile, 0, SEEK_END); + jsonSize = ftell(jsonFile); + fseek(jsonFile, 0, SEEK_SET); - bool done = a && b; + jsonBuffer = new char[jsonSize]; + fread(jsonBuffer, 1, jsonSize, jsonFile); - write(fd, &done, sizeof(bool)); + fclose(jsonFile); + } + + write(fd, &jsonSize, sizeof(long)); + write(fd, jsonBuffer, jsonSize); + + delete[] jsonBuffer; } REGISTER_ZYGISK_MODULE(PlayIntegrityFix) diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java index f9ffab3..38bf0bd 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java @@ -14,7 +14,8 @@ import java.util.Date; import java.util.Enumeration; public class CustomKeyStoreSpi extends KeyStoreSpi { - public static volatile KeyStoreSpi keyStoreSpi; + public static KeyStoreSpi keyStoreSpi; + @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { @@ -23,14 +24,8 @@ public class CustomKeyStoreSpi extends KeyStoreSpi { @Override public Certificate[] engineGetCertificateChain(String alias) { - - if (EntryPoint.isDroidGuard()) { - - EntryPoint.LOG("engineGetCertificateChain call! Throw exception"); - throw new UnsupportedOperationException(); - } - - return keyStoreSpi.engineGetCertificateChain(alias); + EntryPoint.LOG("Tried to get certificate chain, throwing exception!"); + throw new UnsupportedOperationException(); } @Override diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java index 0b02a6f..77fec53 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -2,7 +2,7 @@ package es.chiteroman.playintegrityfix; import java.security.Provider; -public class CustomProvider extends Provider { +public final class CustomProvider extends Provider { public CustomProvider(Provider provider) { super(provider.getName(), provider.getVersion(), provider.getInfo()); @@ -14,7 +14,6 @@ public class CustomProvider extends Provider { @Override public synchronized Service getService(String type, String algorithm) { - EntryPoint.LOG("[SERVICE] Type: " + type + " | Algorithm: " + algorithm); EntryPoint.spoofDevice(); diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index 1a09463..07214b3 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -1,66 +1,58 @@ package es.chiteroman.playintegrityfix; import android.os.Build; -import android.util.JsonReader; import android.util.Log; -import java.io.StringReader; +import org.json.JSONException; +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.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -public class EntryPoint { - private static final Map map = new HashMap<>(); +public final class EntryPoint { + private static JSONObject jsonObject = new JSONObject(); public static void init(String json) { - try (JsonReader reader = new JsonReader(new StringReader(json))) { - reader.beginObject(); - while (reader.hasNext()) { - String key = reader.nextName(); - String value = reader.nextString(); - map.put(key, value); - } - reader.endObject(); - } catch (Exception e) { - LOG("Error parsing JSON: " + e); + try { + jsonObject = new JSONObject(json); + } catch (JSONException e) { + LOG("Couldn't parse JSON from Zygisk"); } - LOG("Map info (keys and values):"); - map.forEach((s, s2) -> LOG(String.format("[%s] -> %s", s, s2))); + boolean FORCE_BASIC_ATTESTATION = true; + + if (jsonObject.has("FORCE_BASIC_ATTESTATION")) { + try { + FORCE_BASIC_ATTESTATION = jsonObject.getBoolean("FORCE_BASIC_ATTESTATION"); + } catch (JSONException e) { + LOG("Couldn't parse FORCE_BASIC_ATTESTATION from JSON"); + } + jsonObject.remove("FORCE_BASIC_ATTESTATION"); + } spoofDevice(); - spoofProvider(); + + if (FORCE_BASIC_ATTESTATION) spoofProvider(); } - protected static void LOG(String msg) { + static void LOG(String msg) { Log.d("PIF/Java", msg); } - protected static void spoofDevice() { - map.forEach(EntryPoint::setFieldValue); - } - - protected static boolean isDroidGuard() { - return Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.US).contains("droidguard")); + static void spoofDevice() { + jsonObject.keys().forEachRemaining(s -> { + try { + Object value = jsonObject.get(s); + setFieldValue(s, value); + } catch (JSONException ignored) { + } + }); } 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); - field.setAccessible(false); - Provider provider = Security.getProvider("AndroidKeyStore"); Provider customProvider = new CustomProvider(provider); @@ -70,14 +62,18 @@ public class EntryPoint { LOG("Spoof KeyStoreSpi and Provider done!"); - } catch (Exception e) { - LOG("spoofProvider exception: " + e); + } catch (Throwable t) { + LOG("spoofProvider exception: " + t); } } - private static void setFieldValue(String name, String value) { - if (name == null || value == null || name.isEmpty() || value.isEmpty()) return; + private static void setFieldValue(String name, Object value) { + if (name == null || value == null || name.isEmpty()) return; + + if (value instanceof String str) if (str.isEmpty() || str.isBlank()) return; + Field field = null; + try { field = Build.class.getDeclaredField(name); } catch (NoSuchFieldException e) { @@ -87,16 +83,23 @@ public class EntryPoint { LOG("Couldn't find field: " + e); } } + if (field == null) return; + field.setAccessible(true); - String oldValue = null; try { - oldValue = (String) field.get(null); - if (value.equals(oldValue)) return; - field.set(null, value); + + Object oldValue = field.get(null); + + if (!value.equals(oldValue)) { + + field.set(null, value); + LOG("Set [" + name + "] field value to [" + value + "]"); + } + } catch (IllegalAccessException e) { - LOG("Couldn't get or set field: " + e); + LOG("Couldn't modify field :("); } - LOG(String.format("Field '%s' with value '%s' is now set to '%s'", name, oldValue, value)); + field.setAccessible(false); } } \ No newline at end of file diff --git a/changelog.md b/changelog.md index 395ea63..5d638d2 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.9 +# v15.0 -- Fix DEVICE verdict not passing with some fingerprints. \ No newline at end of file +- Fix issues. \ No newline at end of file diff --git a/module/boot-completed.sh b/module/boot-completed.sh deleted file mode 100644 index 776f721..0000000 --- a/module/boot-completed.sh +++ /dev/null @@ -1,32 +0,0 @@ -resetprop_if_diff() { - local NAME=$1 - local EXPECTED=$2 - local CURRENT=$(resetprop $NAME) - - [ -z "$CURRENT" ] || [ "$CURRENT" == "$EXPECTED" ] || resetprop $NAME $EXPECTED -} - -resetprop_if_match() { - local NAME=$1 - local CONTAINS=$2 - local VALUE=$3 - - [[ "$(resetprop $NAME)" == *"$CONTAINS"* ]] && resetprop $NAME $VALUE -} - -# Avoid breaking Realme fingerprint scanners -resetprop_if_diff ro.boot.flash.locked 1 - -# Avoid breaking Oppo fingerprint scanners -resetprop_if_diff ro.boot.vbmeta.device_state locked - -# Avoid breaking OnePlus display modes/fingerprint scanners -resetprop_if_diff vendor.boot.verifiedbootstate green - -# Avoid breaking OnePlus/Oppo display fingerprint scanners on OOS/ColorOS 12+ -resetprop_if_diff ro.boot.verifiedbootstate green -resetprop_if_diff ro.boot.veritymode enforcing -resetprop_if_diff vendor.boot.vbmeta.device_state locked - -# Restrict permissions to socket file -chmod 440 /proc/net/unix diff --git a/module/customize.sh b/module/customize.sh index c06cea7..d0a7764 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -52,7 +52,7 @@ if [ -d "/system/app/EliteDevelopmentModule" ]; then ui_print "- EliteDevelopmentModule app removed." fi -# Move pif.json file +# Move empty pif.json file if [ ! -f "/data/adb/pif.json" ]; then mv -f $MODPATH/pif.json /data/adb/ fi diff --git a/module/module.prop b/module/module.prop index d76e8d0..507ae4c 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=playintegrityfix name=Play Integrity Fix -version=v14.9 -versionCode=14900 +version=v15.0 +versionCode=15000 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/module/pif.json b/module/pif.json index f88ada3..e104fae 100644 --- a/module/pif.json +++ b/module/pif.json @@ -1,11 +1,12 @@ { - "PRODUCT": "", - "DEVICE": "", - "MANUFACTURER": "", - "BRAND": "", - "MODEL": "", - "FINGERPRINT": "", - "FIRST_API_LEVEL": 21, - "SECURITY_PATCH": "", - "BUILD_ID": "" + "PRODUCT": "", + "DEVICE": "", + "MANUFACTURER": "", + "BRAND": "", + "MODEL": "", + "FINGERPRINT": "", + "SECURITY_PATCH": "", + "ID": "", + "FIRST_API_LEVEL": 24, + "FORCE_BASIC_ATTESTATION": true } diff --git a/module/service.sh b/module/service.sh index 802eca9..887e4e2 100644 --- a/module/service.sh +++ b/module/service.sh @@ -32,29 +32,21 @@ if [ "$(toybox cat /sys/fs/selinux/enforce)" == "0" ]; then chmod 440 /sys/fs/selinux/policy fi -# KernelSU handles boot completed state in different file. -if [ -z "$KSU" ] || [ "$KSU" = false ]; then - { - # late props which must be set after boot_completed for various OEMs - until [ "$(resetprop sys.boot_completed)" == "1" ]; do - sleep 1 - done +# late props which must be set after boot_completed for various OEMs +until [ "$(resetprop sys.boot_completed)" == "1" ]; do + sleep 1 +done - # Avoid breaking Realme fingerprint scanners - resetprop_if_diff ro.boot.flash.locked 1 +# Avoid breaking Realme fingerprint scanners +resetprop_if_diff ro.boot.flash.locked 1 +# Avoid breaking Oppo fingerprint scanners +resetprop_if_diff ro.boot.vbmeta.device_state locked +# Avoid breaking OnePlus display modes/fingerprint scanners +resetprop_if_diff vendor.boot.verifiedbootstate green +# Avoid breaking OnePlus/Oppo display fingerprint scanners on OOS/ColorOS 12+ +resetprop_if_diff ro.boot.verifiedbootstate green +resetprop_if_diff ro.boot.veritymode enforcing +resetprop_if_diff vendor.boot.vbmeta.device_state locked - # Avoid breaking Oppo fingerprint scanners - resetprop_if_diff ro.boot.vbmeta.device_state locked - - # Avoid breaking OnePlus display modes/fingerprint scanners - resetprop_if_diff vendor.boot.verifiedbootstate green - - # Avoid breaking OnePlus/Oppo display fingerprint scanners on OOS/ColorOS 12+ - resetprop_if_diff ro.boot.verifiedbootstate green - resetprop_if_diff ro.boot.veritymode enforcing - resetprop_if_diff vendor.boot.vbmeta.device_state locked - - # Restrict permissions to socket file - chmod 440 /proc/net/unix - }& -fi +# Restrict permissions to socket file +chmod 440 /proc/net/unix diff --git a/update.json b/update.json index 0f7bcd8..929b558 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v14.9", - "versionCode": 14900, - "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v14.9/PlayIntegrityFix_v14.9.zip", + "version": "v15.0", + "versionCode": 15000, + "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.0/PlayIntegrityFix_v15.0.zip", "changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md" } \ No newline at end of file