From 58db03d596b780c3cbdbfc4f343218ba53b1107a Mon Sep 17 00:00:00 2001 From: 4h9fbZ <7454974439@proton.me> Date: Sun, 21 Jul 2024 01:06:57 +0200 Subject: [PATCH] Implement system signature spoofing --- app/build.gradle.kts | 1 + app/proguard-rules.pro | 1 + .../CustomPackageInfoCreator.java | 41 ++++++++ .../playintegrityfix/EntryPoint.java | 95 +++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 app/src/main/java/es/chiteroman/playintegrityfix/CustomPackageInfoCreator.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 17bbea5..65c6710 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -63,6 +63,7 @@ android { dependencies { implementation("org.lsposed.libcxx:libcxx:27.0.12077973") + implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3") } tasks.register("copyFiles") { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 17b7509..f7164a7 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,3 +1,4 @@ -keep class es.chiteroman.playintegrityfix.EntryPoint {public ;} -keep class es.chiteroman.playintegrityfix.CustomProvider -keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi +-keep class es.chiteroman.playintegrityfix.CustomPackageInfoCreator diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomPackageInfoCreator.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomPackageInfoCreator.java new file mode 100644 index 0000000..a1dc149 --- /dev/null +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomPackageInfoCreator.java @@ -0,0 +1,41 @@ +package es.chiteroman.playintegrityfix; + +import android.content.pm.PackageInfo; +import android.content.pm.Signature; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +public class CustomPackageInfoCreator implements Parcelable.Creator { + private final Parcelable.Creator originalCreator; + private final Signature spoofedSignature; + + public CustomPackageInfoCreator(Parcelable.Creator originalCreator, Signature spoofedSignature) { + this.originalCreator = originalCreator; + this.spoofedSignature = spoofedSignature; + } + + @Override + public PackageInfo createFromParcel(Parcel source) { + PackageInfo packageInfo = originalCreator.createFromParcel(source); + if (packageInfo.packageName.equals("android")) { + if (packageInfo.signatures != null && packageInfo.signatures.length > 0) { + packageInfo.signatures[0] = spoofedSignature; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (packageInfo.signingInfo != null) { + Signature[] signaturesArray = packageInfo.signingInfo.getApkContentsSigners(); + if (signaturesArray != null && signaturesArray.length > 0) { + signaturesArray[0] = spoofedSignature; + } + } + } + } + return packageInfo; + } + + @Override + public PackageInfo[] newArray(int size) { + return originalCreator.newArray(size); + } +} \ No newline at end of file diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index 11a9f95..35a7382 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -1,12 +1,21 @@ package es.chiteroman.playintegrityfix; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Base64; import android.util.JsonReader; import android.util.Log; +import org.lsposed.hiddenapibypass.HiddenApiBypass; + import java.io.IOException; import java.io.StringReader; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.KeyStoreSpi; @@ -18,6 +27,34 @@ import java.util.Map; public final class EntryPoint { private static Integer verboseLogs = 0; + private static final String signatureData = "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\n" + + "F7Xt\n"; + private static final Map map = new HashMap<>(); public static Integer getVerboseLogs() { @@ -28,6 +65,7 @@ public final class EntryPoint { verboseLogs = level; if (verboseLogs > 99) logFields(); spoofProvider(); + spoofPackageManager(); spoofDevice(); } @@ -78,6 +116,63 @@ public final class EntryPoint { } } + private static void spoofPackageManager() { + Signature spoofedSignature = new Signature(Base64.decode(signatureData, Base64.DEFAULT)); + Parcelable.Creator originalCreator = PackageInfo.CREATOR; + Parcelable.Creator customCreator = new CustomPackageInfoCreator(originalCreator, spoofedSignature); + + try { + Field creatorField = findField(PackageInfo.class, "CREATOR"); + creatorField.setAccessible(true); + creatorField.set(null, customCreator); + } catch (Exception e) { + LOG("Couldn't replace PackageInfoCreator: " + e); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;", "Landroid/content/pm", "Landroid/app"); + } + + try { + Field cacheField = findField(PackageManager.class, "sPackageInfoCache"); + cacheField.setAccessible(true); + Object cache = cacheField.get(null); + Method clearMethod = cache.getClass().getMethod("clear"); + clearMethod.invoke(cache); + } catch (Exception e) { + LOG("Couldn't clear PackageInfoCache: " + e); + } + + try { + Field creatorsField = findField(Parcel.class, "mCreators"); + creatorsField.setAccessible(true); + Map mCreators = (Map) creatorsField.get(null); + mCreators.clear(); + } catch (Exception e) { + LOG("Couldn't clear Parcel mCreators: " + e); + } + + try { + Field creatorsField = findField(Parcel.class, "sPairedCreators"); + creatorsField.setAccessible(true); + Map sPairedCreators = (Map) creatorsField.get(null); + sPairedCreators.clear(); + } catch (Exception e) { + LOG("Couldn't clear Parcel sPairedCreators: " + e); + } + } + + private static Field findField(Class currentClass, String fieldName) throws NoSuchFieldException { + while (currentClass != null && !currentClass.equals(Object.class)) { + try { + return currentClass.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + currentClass = currentClass.getSuperclass(); + } + } + throw new NoSuchFieldException("Field '" + fieldName + "' not found in class hierarchy of " + currentClass.getName()); + } + private static boolean classContainsField(Class className, String fieldName) { for (Field field : className.getDeclaredFields()) { if (field.getName().equals(fieldName)) return true;