From 9c6f065bb603ca7a21eca813659cd7a7deb3801f Mon Sep 17 00:00:00 2001 From: gavdoc38 Date: Sat, 30 Aug 2025 19:02:50 +0100 Subject: [PATCH] Add advanced setting spoofVendingFingerprint (#38) spoofVendingFingerprint = 0 / 1 When 0, no impact on Vending When 1, same FINGERPRINT from custom.pif.json is injected into Vending Unless spoofVendingSdk is enabled also, in which case FINGERPRINT is not injected since it's not used --- app/src/main/cpp/main.cpp | 22 +++++-- .../playintegrityfix/EntryPointVending.java | 64 +++++++++++++------ module/autopif2.sh | 2 +- module/example.pif.json | 1 + module/migrate.sh | 3 +- 5 files changed, 65 insertions(+), 27 deletions(-) diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index e6f8435..54b3fad 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -20,6 +20,7 @@ static int spoofBuild = 1; static int spoofProps = 1; static int spoofProvider = 1; static int spoofSignature = 0; +static int spoofVendingFingerprint = 0; static int spoofVendingSdk = 0; static std::map jsonProps; @@ -166,10 +167,10 @@ public: readJson(); if (pkgName == VENDING_PACKAGE) spoofProps = spoofBuild = spoofProvider = spoofSignature = 0; - else spoofVendingSdk = 0; + else spoofVendingFingerprint = spoofVendingSdk = 0; if (spoofProps > 0) doHook(); - if (spoofBuild + spoofProvider + spoofSignature + spoofVendingSdk > 0) inject(); + if (spoofBuild + spoofProvider + spoofSignature + spoofVendingFingerprint + spoofVendingSdk > 0) inject(); dexVector.clear(); json.clear(); @@ -185,6 +186,7 @@ private: std::vector dexVector; nlohmann::json json; std::string pkgName; + std::string spoofFingerprintValue = ""; void readJson() { LOGD("JSON contains %d keys!", static_cast(json.size())); @@ -210,6 +212,17 @@ private: } json.erase("spoofVendingSdk"); } + if (json.contains("spoofVendingFingerprint")) { + if (!json["spoofVendingFingerprint"].is_null() && json["spoofVendingFingerprint"].is_string() && json["spoofVendingFingerprint"] != "" && + json.contains("FINGERPRINT") && !json["FINGERPRINT"].is_null() && json["FINGERPRINT"].is_string() && json["FINGERPRINT"] != "") { + spoofVendingFingerprint = stoi(json["spoofVendingFingerprint"].get()); + spoofFingerprintValue = json["FINGERPRINT"].get(); + if (verboseLogs > 0) LOGD("Spoofing Fingerprint in Play Store %s!", (spoofVendingFingerprint > 0) ? "enabled" : "disabled"); + } else { + LOGD("Error parsing spoofVendingFingerprint or FINGERPRINT field!"); + } + json.erase("spoofVendingFingerprint"); + } if (pkgName == VENDING_PACKAGE) { json.clear(); return; @@ -299,8 +312,9 @@ private: if (pkgName == VENDING_PACKAGE) { LOGD("JNI %s: Calling EntryPointVending.init", niceName); - auto entryInit = env->GetStaticMethodID(entryClass, "init", "(II)V"); - env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofVendingSdk); + auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIILjava/lang/String;)V"); + auto javaStr = env->NewStringUTF(spoofFingerprintValue.c_str()); + env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofVendingFingerprint, spoofVendingSdk, javaStr); } else { LOGD("JNI %s: Sending JSON", niceName); auto receiveJson = env->GetStaticMethodID(entryClass, "receiveJson", "(Ljava/lang/String;)V"); diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPointVending.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPointVending.java index ec38bea..02271ba 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPointVending.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPointVending.java @@ -12,29 +12,51 @@ public final class EntryPointVending { } @SuppressLint("DefaultLocale") - public static void init(int verboseLogs, int spoofVendingSdk) { - if (spoofVendingSdk < 1) return; - int requestSdk = spoofVendingSdk == 1 ? 32 : spoofVendingSdk; - int targetSdk = Math.min(Build.VERSION.SDK_INT, requestSdk); - int oldValue; - try { - Field field = Build.VERSION.class.getDeclaredField("SDK_INT"); - field.setAccessible(true); - oldValue = field.getInt(null); - if (oldValue == targetSdk) { - if (verboseLogs > 2) LOG(String.format("[SDK_INT]: %d (unchanged)", oldValue)); + public static void init(int verboseLogs, int spoofVendingFingerprint, int spoofVendingSdk, String spoofFingerprintValue) { + if (spoofVendingSdk < 1){ + // Only spoof FINGERPRINT to Play Store if not forcing legacy verdict + if (spoofVendingFingerprint < 1) return; + String oldValue; + try { + Field field = Build.class.getDeclaredField("FINGERPRINT"); + field.setAccessible(true); + oldValue = String.valueOf(field.get(null)); + if (oldValue.equals(spoofFingerprintValue)) { + if (verboseLogs > 2) LOG(String.format("[FINGERPRINT]: %s (unchanged)", oldValue)); + field.setAccessible(false); + return; + } + field.set(null, spoofFingerprintValue); field.setAccessible(false); - return; + LOG(String.format("[FINGERPRINT]: %s -> %s", oldValue, spoofFingerprintValue)); + } catch (NoSuchFieldException e) { + LOG("FINGERPRINT field not found: " + e); + } catch (SecurityException | IllegalAccessException | IllegalArgumentException | + NullPointerException | ExceptionInInitializerError e) { + LOG("FINGERPRINT field not accessible: " + e); + } + } else { + int requestSdk = spoofVendingSdk == 1 ? 32 : spoofVendingSdk; + int targetSdk = Math.min(Build.VERSION.SDK_INT, requestSdk); + int oldValue; + try { + Field field = Build.VERSION.class.getDeclaredField("SDK_INT"); + field.setAccessible(true); + oldValue = field.getInt(null); + if (oldValue == targetSdk) { + if (verboseLogs > 2) LOG(String.format("[SDK_INT]: %d (unchanged)", oldValue)); + field.setAccessible(false); + return; + } + field.set(null, targetSdk); + field.setAccessible(false); + LOG(String.format("[SDK_INT]: %d -> %d", oldValue, targetSdk)); + } catch (NoSuchFieldException e) { + LOG("SDK_INT field not found: " + e); + } catch (SecurityException | IllegalAccessException | IllegalArgumentException | + NullPointerException | ExceptionInInitializerError e) { + LOG("SDK_INT field not accessible: " + e); } - field.set(null, targetSdk); - field.setAccessible(false); - LOG(String.format("[SDK_INT]: %d -> %d", oldValue, targetSdk)); - } catch (NoSuchFieldException e) { - LOG("SDK_INT field not found: " + e); - } catch (SecurityException | IllegalAccessException | IllegalArgumentException | - NullPointerException | ExceptionInInitializerError e) { - LOG("SDK_INT field not accessible: " + e); - } } } diff --git a/module/autopif2.sh b/module/autopif2.sh index da9b760..7a187b2 100644 --- a/module/autopif2.sh +++ b/module/autopif2.sh @@ -162,7 +162,7 @@ if [ -f "$MIGRATE" ]; then if [ -n "$ARGS" ]; then grep_json() { [ -f "$2" ] && grep -m1 "$1" $2 | cut -d\" -f4; } verboseLogs=$(grep_json "VERBOSE_LOGS" $OLDJSON); - ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs"; + ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingFingerprint spoofVendingSdk verboseLogs"; for SETTING in $ADVSETTINGS; do eval [ -z \"\$$SETTING\" ] \&\& $SETTING=$(grep_json "$SETTING" $OLDJSON); eval TMPVAL=\$$SETTING; diff --git a/module/example.pif.json b/module/example.pif.json index 7036f41..a1ed701 100644 --- a/module/example.pif.json +++ b/module/example.pif.json @@ -31,6 +31,7 @@ "spoofProps": "1", "spoofProvider": "1", "spoofSignature": "0", + "spoofVendingFingerprint": "0", "spoofVendingSdk": "0", "verboseLogs": "0" } diff --git a/module/migrate.sh b/module/migrate.sh index 39c6c32..aad351a 100644 --- a/module/migrate.sh +++ b/module/migrate.sh @@ -114,12 +114,13 @@ if [ -z "$DEVICE_INITIAL_SDK_INT" -o "$DEVICE_INITIAL_SDK_INT" = "null" ]; then DEVICE_INITIAL_SDK_INT=25; fi; -ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs"; +ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingFingerprint spoofVendingSdk verboseLogs"; spoofBuild=1; spoofProps=1; spoofProvider=1; spoofSignature=0; +spoofVendingFingerprint=0; spoofVendingSdk=0; verboseLogs=0;