Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
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
  • Loading branch information
TheFreeman193 committed Feb 4, 2025
commit 5ffeb5a405dc46302bef3763426345bf53991ef0
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

## 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.

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

Expand Down
1 change: 1 addition & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
-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.CustomKeyStoreSpi
-keep class es.chiteroman.playintegrityfix.CustomPackageInfoCreator
57 changes: 42 additions & 15 deletions app/src/main/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@

#define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/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 spoofBuild = 1;
static int spoofProps = 1;
static int spoofProvider = 1;
static int spoofSignature = 0;
static int spoofVendingSdk = 0;

static std::map<std::string, std::string> jsonProps;

Expand Down Expand Up @@ -95,11 +97,11 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
return;
}

std::string_view process(rawProcess);
pkgName = rawProcess;
std::string_view dir(rawDir);

isGms = dir.ends_with("/com.google.android.gms");
isGmsUnstable = process == "com.google.android.gms.unstable";
isGms = dir.ends_with("/com.google.android.gms") || dir.ends_with("/com.android.vending");
isGmsUnstable = pkgName == "com.google.android.gms.unstable" || pkgName == VENDING_PACKAGE;

env->ReleaseStringUTFChars(args->nice_name, rawProcess);
env->ReleaseStringUTFChars(args->app_data_dir, rawDir);
Expand Down Expand Up @@ -161,6 +163,10 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
if (dexVector.empty() || json.empty()) return;

readJson();

if (pkgName == VENDING_PACKAGE) spoofProps = spoofBuild = spoofProvider = 0;
else spoofVendingSdk = 0;

if (spoofProps > 0) doHook();
inject();

Expand All @@ -177,6 +183,7 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
JNIEnv *env = nullptr;
std::vector<char> dexVector;
nlohmann::json json;
std::string pkgName;

void readJson() {
LOGD("JSON contains %d keys!", static_cast<int>(json.size()));
Expand All @@ -193,6 +200,19 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
}

// 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["spoofBuild"].is_null() && json["spoofBuild"].is_string() && json["spoofBuild"] != "") {
spoofBuild = stoi(json["spoofBuild"].get<std::string>());
Expand Down Expand Up @@ -255,32 +275,39 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
}

void inject() {
LOGD("JNI: Getting system classloader");
LOGD("JNI %s: Getting system classloader", pkgName.c_str());
auto clClass = env->FindClass("java/lang/ClassLoader");
auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
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 dexClInit = env->GetMethodID(dexClClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
auto buffer = env->NewDirectByteBuffer(dexVector.data(), static_cast<jlong>(dexVector.size()));
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 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 entryClass = (jclass) entryClassObj;

LOGD("JNI: Sending JSON");
auto receiveJson = env->GetStaticMethodID(entryClass, "receiveJson", "(Ljava/lang/String;)V");
auto javaStr = env->NewStringUTF(json.dump().c_str());
env->CallStaticVoidMethod(entryClass, receiveJson, javaStr);

LOGD("JNI: Calling init");
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature);
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 javaStr = env->NewStringUTF(json.dump().c_str());
env->CallStaticVoidMethod(entryClass, receiveJson, javaStr);

LOGD("JNI %s: Calling EntryPoint.init", pkgName.c_str());
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature);
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,6 @@ private static void logFields() {
}

static void LOG(String msg) {
Log.d("PIF/Java", msg);
Log.d("PIF/Java:DG", msg);
}
}
Original file line number Diff line number Diff line change
@@ -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);

}
}
}
8 changes: 4 additions & 4 deletions module/autopif2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,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 verboseLogs";
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs";
for SETTING in $ADVSETTINGS; do
eval [ -z \"\$$SETTING\" ] \&\& $SETTING=$(grep_json "$SETTING" $OLDJSON);
eval TMPVAL=\$$SETTING;
Expand All @@ -182,8 +182,8 @@ if [ "$DIR" = /data/adb/modules/playintegrityfix/autopif2 ]; then
fi;
item "Installing new json ...";
cp -fv $NEWNAME ..;
if [ -f /data/adb/modules/playintegrityfix/killgms.sh ]; then
item "Killing any running GMS DroidGuard process ...";
sh /data/adb/modules/playintegrityfix/killgms.sh 2>&1 || true;
if [ -f /data/adb/modules/playintegrityfix/killpi.sh ]; then
item "Killing any running GMS DroidGuard/Play Store processes ...";
sh /data/adb/modules/playintegrityfix/killpi.sh 2>&1 || true;
fi;
fi;
12 changes: 0 additions & 12 deletions module/killgms.sh

This file was deleted.

13 changes: 13 additions & 0 deletions module/killpi.sh
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 2 additions & 1 deletion module/migrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 verboseLogs";
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature spoofVendingSdk verboseLogs";

spoofBuild=1;
spoofProps=1;
spoofProvider=1;
spoofSignature=0;
spoofVendingSdk=0;
verboseLogs=0;

if [ -f "$OUT" ]; then
Expand Down