Add spoofVendingSdk to force legacy verdicts from new PI

Squashed: Add spoofVendingSdk for forcing new PI legacy verdicts
Squashed: Move vending SDK spoof to EntryPointVending, replace killgms.sh with killpi.sh
This commit is contained in:
Nicholas Bissell
2025-02-01 20:59:52 +00:00
committed by Chris Renshaw
parent e2b8a3c4b0
commit 4740d2048d
9 changed files with 103 additions and 34 deletions

View File

@@ -56,13 +56,13 @@ A migration may also be performed manually with `sh migrate.sh` and custom.pif.j
You can customize the included default [example.app_replace.list](https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/module/example.app_replace.list) from the module directory (/data/adb/modules/playintegrityfix) then rename it to custom.app_replace.list to systemlessly replace any additional conflicting custom ROM spoof injection app paths to disable them. You can customize the included default [example.app_replace.list](https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/module/example.app_replace.list) from the module directory (/data/adb/modules/playintegrityfix) then rename it to custom.app_replace.list to systemlessly replace any additional conflicting custom ROM spoof injection app paths to disable them.
## About 'autopif2.sh' and 'killgms.sh' script files ## About 'autopif2.sh' and 'killpi.sh' script files
There's intentionally no pif.json in the module because the goal remains to be futureproof, and including something that may be banned and obsolete within days would be contrary to that goal. If you don't care to have your own private fingerprint to use or don't have time to look for one currently then simply run the generation script from a root manager app that supports the module Action button, a root prompt with `sh autopif2.sh` in the module directory (/data/adb/modules/playintegrityfix), or from a file explorer app that supports script execution. There's intentionally no pif.json in the module because the goal remains to be futureproof, and including something that may be banned and obsolete within days would be contrary to that goal. If you don't care to have your own private fingerprint to use or don't have time to look for one currently then simply run the generation script from a root manager app that supports the module Action button, a root prompt with `sh autopif2.sh` in the module directory (/data/adb/modules/playintegrityfix), or from a file explorer app that supports script execution.
The autopif2 script generates a random device fingerprint from the latest Pixel Beta, ideally only to test an initial setup, since they expire roughly every 6 weeks from the Pixel Beta release date (dates included in the generated fingerprint), and the public mass-used ones from other modules or ROMs may also get banned or may be banned for RCS use while otherwise passing Play Integrity and SafetyNet in that time. The autopif2 script generates a random device fingerprint from the latest Pixel Beta, ideally only to test an initial setup, since they expire roughly every 6 weeks from the Pixel Beta release date (dates included in the generated fingerprint), and the public mass-used ones from other modules or ROMs may also get banned or may be banned for RCS use while otherwise passing Play Integrity and SafetyNet in that time.
The killgms script forces the Google Play Services DroidGuard process (com.google.android.gms.unstable) to end, making it restart with the next attestation attempt; useful for testing out different fingerprints without requiring a reboot in between. The killpi script forces the Google Play Services DroidGuard (com.google.android.gms.unstable) and Play Store (com.android.vending) processes to end, making them restart with the next attestation attempt; useful for testing out different fingerprints without requiring a reboot in between.
## Troubleshooting ## Troubleshooting

View File

@@ -1,4 +1,5 @@
-keep class es.chiteroman.playintegrityfix.EntryPoint {public <methods>;} -keep class es.chiteroman.playintegrityfix.EntryPoint {public <methods>;}
-keep class es.chiteroman.playintegrityfix.EntryPointVending {public <methods>;}
-keep class es.chiteroman.playintegrityfix.CustomProvider -keep class es.chiteroman.playintegrityfix.CustomProvider
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi -keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi
-keep class es.chiteroman.playintegrityfix.CustomPackageInfoCreator -keep class es.chiteroman.playintegrityfix.CustomPackageInfoCreator

View File

@@ -12,12 +12,14 @@
#define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json" #define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json"
#define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json" #define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json"
#define VENDING_PACKAGE "com.android.vending"
static int verboseLogs = 0; static int verboseLogs = 0;
static int spoofBuild = 1; static int spoofBuild = 1;
static int spoofProps = 1; static int spoofProps = 1;
static int spoofProvider = 1; static int spoofProvider = 1;
static int spoofSignature = 0; static int spoofSignature = 0;
static int spoofVendingSdk = 0;
static std::map<std::string, std::string> jsonProps; static std::map<std::string, std::string> jsonProps;
@@ -95,11 +97,11 @@ public:
return; return;
} }
std::string_view process(rawProcess); pkgName = rawProcess;
std::string_view dir(rawDir); std::string_view dir(rawDir);
isGms = dir.ends_with("/com.google.android.gms"); isGms = dir.ends_with("/com.google.android.gms") || dir.ends_with("/com.android.vending");
isGmsUnstable = process == "com.google.android.gms.unstable"; isGmsUnstable = pkgName == "com.google.android.gms.unstable" || pkgName == VENDING_PACKAGE;
env->ReleaseStringUTFChars(args->nice_name, rawProcess); env->ReleaseStringUTFChars(args->nice_name, rawProcess);
env->ReleaseStringUTFChars(args->app_data_dir, rawDir); env->ReleaseStringUTFChars(args->app_data_dir, rawDir);
@@ -161,6 +163,10 @@ public:
if (dexVector.empty() || json.empty()) return; if (dexVector.empty() || json.empty()) return;
readJson(); readJson();
if (pkgName == VENDING_PACKAGE) spoofProps = spoofBuild = spoofProvider = 0;
else spoofVendingSdk = 0;
if (spoofProps > 0) doHook(); if (spoofProps > 0) doHook();
inject(); inject();
@@ -177,6 +183,7 @@ private:
JNIEnv *env = nullptr; JNIEnv *env = nullptr;
std::vector<char> dexVector; std::vector<char> dexVector;
nlohmann::json json; nlohmann::json json;
std::string pkgName;
void readJson() { void readJson() {
LOGD("JSON contains %d keys!", static_cast<int>(json.size())); LOGD("JSON contains %d keys!", static_cast<int>(json.size()));
@@ -193,6 +200,19 @@ private:
} }
// Advanced spoofing settings // Advanced spoofing settings
if (json.contains("spoofVendingSdk")) {
if (!json["spoofVendingSdk"].is_null() && json["spoofVendingSdk"].is_string() && json["spoofVendingSdk"] != "") {
spoofVendingSdk = stoi(json["spoofVendingSdk"].get<std::string>());
if (verboseLogs > 0) LOGD("Spoofing SDK Level in Play Store %s!", (spoofVendingSdk > 0) ? "enabled" : "disabled");
} else {
LOGD("Error parsing spoofVendingSdk!");
}
json.erase("spoofVendingSdk");
}
if (pkgName == VENDING_PACKAGE) {
json.clear();
return;
}
if (json.contains("spoofBuild")) { if (json.contains("spoofBuild")) {
if (!json["spoofBuild"].is_null() && json["spoofBuild"].is_string() && json["spoofBuild"] != "") { if (!json["spoofBuild"].is_null() && json["spoofBuild"].is_string() && json["spoofBuild"] != "") {
spoofBuild = stoi(json["spoofBuild"].get<std::string>()); spoofBuild = stoi(json["spoofBuild"].get<std::string>());
@@ -255,33 +275,40 @@ private:
} }
void inject() { void inject() {
LOGD("JNI: Getting system classloader"); LOGD("JNI %s: Getting system classloader", pkgName.c_str());
auto clClass = env->FindClass("java/lang/ClassLoader"); auto clClass = env->FindClass("java/lang/ClassLoader");
auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader);
LOGD("JNI: Creating module classloader"); LOGD("JNI %s: Creating module classloader", pkgName.c_str());
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
auto dexClInit = env->GetMethodID(dexClClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); auto dexClInit = env->GetMethodID(dexClClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
auto buffer = env->NewDirectByteBuffer(dexVector.data(), static_cast<jlong>(dexVector.size())); auto buffer = env->NewDirectByteBuffer(dexVector.data(), static_cast<jlong>(dexVector.size()));
auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader);
LOGD("JNI: Loading module class"); LOGD("JNI %s: Loading module class", pkgName.c_str());
auto loadClass = env->GetMethodID(clClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); auto loadClass = env->GetMethodID(clClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
auto entryClassName = env->NewStringUTF("es.chiteroman.playintegrityfix.EntryPoint"); const char* className = pkgName == VENDING_PACKAGE ? "es.chiteroman.playintegrityfix.EntryPointVending" : "es.chiteroman.playintegrityfix.EntryPoint";
auto entryClassName = env->NewStringUTF(className);
auto entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName); auto entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName);
auto entryClass = (jclass) entryClassObj; auto entryClass = (jclass) entryClassObj;
LOGD("JNI: Sending JSON"); if (pkgName == VENDING_PACKAGE) {
LOGD("JNI %s: Calling EntryPointVending.init", pkgName.c_str());
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(II)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofVendingSdk);
} else {
LOGD("JNI %s: Sending JSON", pkgName.c_str());
auto receiveJson = env->GetStaticMethodID(entryClass, "receiveJson", "(Ljava/lang/String;)V"); auto receiveJson = env->GetStaticMethodID(entryClass, "receiveJson", "(Ljava/lang/String;)V");
auto javaStr = env->NewStringUTF(json.dump().c_str()); auto javaStr = env->NewStringUTF(json.dump().c_str());
env->CallStaticVoidMethod(entryClass, receiveJson, javaStr); env->CallStaticVoidMethod(entryClass, receiveJson, javaStr);
LOGD("JNI: Calling init"); LOGD("JNI %s: Calling EntryPoint.init", pkgName.c_str());
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V"); auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature); env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature);
} }
}
}; };
static void companion(int fd) { static void companion(int fd) {

View File

@@ -265,6 +265,6 @@ public final class EntryPoint {
} }
static void LOG(String msg) { static void LOG(String msg) {
Log.d("PIF/Java", msg); Log.d("PIF/Java:DG", msg);
} }
} }

View File

@@ -0,0 +1,39 @@
package es.chiteroman.playintegrityfix;
import android.annotation.SuppressLint;
import android.os.Build;
import java.lang.reflect.Field;
import android.util.Log;
public final class EntryPointVending {
private static void LOG(String msg) {
Log.d("PIF/Java:vending", msg);
}
@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));
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);
}
}
}

View File

@@ -158,7 +158,7 @@ if [ -f "$MIGRATE" ]; then
if [ -n "$ARGS" ]; then if [ -n "$ARGS" ]; then
grep_json() { [ -f "$2" ] && grep -m1 "$1" $2 | cut -d\" -f4; } grep_json() { [ -f "$2" ] && grep -m1 "$1" $2 | cut -d\" -f4; }
verboseLogs=$(grep_json "VERBOSE_LOGS" $OLDJSON); verboseLogs=$(grep_json "VERBOSE_LOGS" $OLDJSON);
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature verboseLogs"; ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs";
for SETTING in $ADVSETTINGS; do for SETTING in $ADVSETTINGS; do
eval [ -z \"\$$SETTING\" ] \&\& $SETTING=$(grep_json "$SETTING" $OLDJSON); eval [ -z \"\$$SETTING\" ] \&\& $SETTING=$(grep_json "$SETTING" $OLDJSON);
eval TMPVAL=\$$SETTING; eval TMPVAL=\$$SETTING;
@@ -182,8 +182,8 @@ if [ "$DIR" = /data/adb/modules/playintegrityfix/autopif2 ]; then
fi; fi;
item "Installing new json ..."; item "Installing new json ...";
cp -fv $NEWNAME ..; cp -fv $NEWNAME ..;
if [ -f /data/adb/modules/playintegrityfix/killgms.sh ]; then if [ -f /data/adb/modules/playintegrityfix/killpi.sh ]; then
item "Killing any running GMS DroidGuard process ..."; item "Killing any running GMS DroidGuard/Play Store processes ...";
sh /data/adb/modules/playintegrityfix/killgms.sh 2>&1 || true; sh /data/adb/modules/playintegrityfix/killpi.sh 2>&1 || true;
fi; fi;
fi; fi;

View File

@@ -1,12 +0,0 @@
#!/system/bin/sh
# killgms.sh by osm0sis @ xda-developers
#
# Kill the Google Play services DroidGuard process
# (com.google.android.gms.unstable)
if [ "$USER" != "root" -a "$(whoami 2>/dev/null)" != "root" ]; then
echo "killgms: need root permissions";
exit 1;
fi;
killall -v com.google.android.gms.unstable;

13
module/killpi.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/system/bin/sh
# killpi.sh by osm0sis @ xda-developers
#
# Kill the Google Play services DroidGuard and Play Store processes
# (com.google.android.gms.unstable and com.android.vending)
if [ "$USER" != "root" -a "$(whoami 2>/dev/null)" != "root" ]; then
echo "killpi: need root permissions";
exit 1;
fi;
killall -v com.google.android.gms.unstable;
killall -v com.android.vending;

View File

@@ -114,12 +114,13 @@ if [ -z "$DEVICE_INITIAL_SDK_INT" -o "$DEVICE_INITIAL_SDK_INT" = "null" ]; then
DEVICE_INITIAL_SDK_INT=25; DEVICE_INITIAL_SDK_INT=25;
fi; fi;
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature verboseLogs"; ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs";
spoofBuild=1; spoofBuild=1;
spoofProps=1; spoofProps=1;
spoofProvider=1; spoofProvider=1;
spoofSignature=0; spoofSignature=0;
spoofVendingSdk=0;
verboseLogs=0; verboseLogs=0;
if [ -f "$OUT" ]; then if [ -f "$OUT" ]; then