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 deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ae388c2..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index c306bb0..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 8978d23..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 25dfc0f..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 58555c7..6d0f324 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,47 @@ # Play Integrity Fix -A Zygisk module which fix "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTEGRITY" (Play Integrity). + +A Zygisk module which fix "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTEGRITY" (Play +Integrity). To use this module you must have one of this: + - Magisk with Zygisk enabled. - KernelSU with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed. [**Download the latest here**](https://github.com/chiteroman/PlayIntegrityFix/releases/latest). +## Telegram group + +https://t.me/playintegrityfix + ## Donations + - [PayPal](https://paypal.me/chiteroman) ## Official posts + - [XDA](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/) ## About module -It injects a classes.dex file to modify few fields in android.os.Build class. Also, in native code it creates a hook to modify system properties. + +It injects a classes.dex file to modify few fields in android.os.Build class. Also, in native code +it creates a hook to modify system properties. The purpose of the module is to avoid a hardware attestation. ## Failing BASIC verdict -If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Integrity) something is wrong in your setup. My recommended steps in order to find the problem: + +If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Integrity) something is +wrong in your setup. My recommended steps in order to find the problem: + - Disable all modules except mine. - Check your SELinux (must be enforced). Some modules which modify system can trigger DroidGuard detection, never hook GMS processes. ## Certify Play Store and fix Google Wallet + Follow this steps: + - Flash my module in Magisk/KernelSU (if you already have my module, just ignore this step). - Clear Google Wallet cache. - Clear Google Play Store cache. @@ -35,44 +51,18 @@ Follow this steps: Reboot and done! ## Read module logs + You can read module logs using this command: + ``` adb shell "logcat | grep 'PIF'" ``` ## Can this module pass MEETS_STRONG_INTEGRITY? -**No** -## Play Integrity is now our problem, SafetyNet is deprecated -You can read more info here: [click me](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/) +No. -## Make FCM Push back to work after cleared GSF data -Once you cleared GSF (Google Service Framework, com.google.android.gsf) data, a new DeviceID of Google Service Framework will be generated. So all the FCM tokens that have registered in the server of Apps will no longer work (it will point to your old DeviceID). You can follow these steps to make the Apps to generate a new FCM token. +## About Play Integrity, SafetyNet is deprecated -The idea is to delete a file called `xxx.gms.appid-no-backup` (xxx usually is the package name) in the app's files folder. Once the file does not exist, the app will generate a new FCM token when it starts up next time. - -Run the following commands to do that, you can use `adb shell`, Termux, some terminal apps, or whatever. - -1. Get the root user -``` -su -``` - -2. cd to `/data/data` -``` -cd /data/data -``` - -3. Search for the files ending with `gms.appid-no-backup` firstly (without really deleting it), so you can review the list of the files that will be deleted, and make sure it will not delete something wrong (usually it should not. I don't think any other useful files named like this). If you don't really care, you can skip this step. -``` -find . -type f -name '*gms.appid-no-backup' -``` - -4. Delete all the files end with `gms.appid-no-backup` -``` -find . -type f -name '*gms.appid-no-backup' -delete -``` - -5. Reboot your device. - -6. It is better to launch the apps that receive FCM push one time, to make sure it generates a new FCM token and registers with the server. +You can read more info +here: [click me](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 46510f1..eab226d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,7 +19,7 @@ android { ndk { //noinspection ChromeOsAbiSupport abiFilters += setOf("armeabi-v7a", "arm64-v8a") - jobs = 4 + jobs = Runtime.getRuntime().availableProcessors() } } } diff --git a/app/src/main/cpp/zygisk.hpp b/app/src/main/cpp/zygisk.hpp index 7272633..7c861ad 100644 --- a/app/src/main/cpp/zygisk.hpp +++ b/app/src/main/cpp/zygisk.hpp @@ -19,7 +19,7 @@ #include -#define ZYGISK_API_VERSION 2 +#define ZYGISK_API_VERSION 4 /* @@ -142,6 +142,7 @@ struct AppSpecializeArgs { jint &gid; jintArray &gids; jint &runtime_flags; + jobjectArray &rlimits; jint &mount_external; jstring &se_info; jstring &nice_name; @@ -149,6 +150,7 @@ struct AppSpecializeArgs { jstring &app_data_dir; // Optional arguments. Please check whether the pointer is null before de-referencing + jintArray *const fds_to_ignore; jboolean *const is_child_zygote; jboolean *const is_top_app; jobjectArray *const pkg_data_info_list; @@ -241,6 +243,14 @@ struct Api { // Returns bitwise-or'd zygisk::StateFlag values. uint32_t getFlags(); + // Exempt the provided file descriptor from being automatically closed. + // + // This API only make sense in preAppSpecialize; calling this method in any other situation + // is either a no-op (returns true) or an error (returns false). + // + // When false is returned, the provided file descriptor will eventually be closed by zygote. + bool exemptFd(int fd); + // Hook JNI native methods for a class // // Lookup all registered JNI native methods and replace it with your own methods. @@ -257,13 +267,10 @@ struct Api { // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64 // (More details: https://man7.org/linux/man-pages/man5/proc.5.html) // - // For ELFs loaded in memory with pathname matching `regex`, replace function `symbol` with `newFunc`. + // The `dev` and `inode` pair uniquely identifies a file being mapped into memory. + // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`. // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. - void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc); - - // For ELFs loaded in memory with pathname matching `regex`, exclude hooks registered for `symbol`. - // If `symbol` is nullptr, then all symbols will be excluded. - void pltHookExclude(const char *regex, const char *symbol); + void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc); // Commit all the hooks that was previously registered. // Returns false if an error occurred. @@ -324,8 +331,8 @@ struct api_table { bool (*registerModule)(api_table *, module_abi *); void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); - void (*pltHookRegister)(const char *, const char *, void *, void **); - void (*pltHookExclude)(const char *, const char *); + void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); + bool (*exemptFd)(int); bool (*pltHookCommit)(); int (*connectCompanion)(void * /* impl */); void (*setOption)(void * /* impl */, Option); @@ -358,14 +365,14 @@ inline void Api::setOption(Option opt) { inline uint32_t Api::getFlags() { return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; } +inline bool Api::exemptFd(int fd) { + return tbl->exemptFd != nullptr && tbl->exemptFd(fd); +} inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods); } -inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { - if (tbl->pltHookRegister) tbl->pltHookRegister(regex, symbol, newFunc, oldFunc); -} -inline void Api::pltHookExclude(const char *regex, const char *symbol) { - if (tbl->pltHookExclude) tbl->pltHookExclude(regex, symbol); +inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) { + if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc); } inline bool Api::pltHookCommit() { return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index 6a90723..04d6157 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -60,10 +60,17 @@ public final class EntryPoint { } public static void spoofDevice() { - props.forEach((field, value) -> setProp((String) field, (String) value)); + setProp("PRODUCT", props.getProperty("PRODUCT")); + setProp("DEVICE", props.getProperty("DEVICE")); + setProp("MANUFACTURER", props.getProperty("MANUFACTURER")); + setProp("BRAND", props.getProperty("BRAND")); + setProp("MODEL", props.getProperty("MODEL")); + setProp("FINGERPRINT", props.getProperty("FINGERPRINT")); + setVersionProp("SECURITY_PATCH", props.getProperty("SECURITY_PATCH")); } private static void setProp(String name, String value) { + if (name == null || value == null || name.isEmpty() || value.isEmpty()) return; try { Field field = Build.class.getDeclaredField(name); field.setAccessible(true); @@ -79,6 +86,23 @@ public final class EntryPoint { } } + private static void setVersionProp(String name, String value) { + if (name == null || value == null || name.isEmpty() || value.isEmpty()) return; + try { + Field field = Build.VERSION.class.getDeclaredField(name); + field.setAccessible(true); + String oldValue = (String) field.get(null); + field.set(null, value); + field.setAccessible(false); + if (value.equals(oldValue)) return; + LOG(String.format("[%s]: %s -> %s", name, oldValue, value)); + } catch (NoSuchFieldException e) { + LOG(String.format("Couldn't find '%s' field name.", name)); + } catch (IllegalAccessException e) { + LOG(String.format("Couldn't modify '%s' field value.", name)); + } + } + public static void LOG(String msg) { Log.d("PIF/Java", msg); } diff --git a/build.gradle.kts b/build.gradle.kts index 7485f0a..3e03b23 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - id("com.android.application") version "8.1.3" apply false + id("com.android.application") version "8.1.4" apply false } \ No newline at end of file