mirror of
https://github.com/chiteroman/PlayIntegrityFix.git
synced 2025-01-19 11:32:39 +02:00
Add SECURITY_PATCH and fix null props
This commit is contained in:
parent
bf9ce4eb73
commit
abc57e0a46
3
.idea/.gitignore
vendored
3
.idea/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel target="17" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="testRunner" value="GRADLE" />
|
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="gradleJvm" value="jbr-17" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
<option value="$PROJECT_DIR$/app" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,20 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="FieldCanBeLocal" enabled="true" level="WARNING" enabled_by_default="true">
|
|
||||||
<option name="EXCLUDE_ANNOS">
|
|
||||||
<value>
|
|
||||||
<list size="1">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="android.annotation.TargetApi" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="IGNORE_FIELDS_USED_IN_MULTIPLE_METHODS" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
|
||||||
<option name="processCode" value="true" />
|
|
||||||
<option name="processLiterals" value="true" />
|
|
||||||
<option name="processComments" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
@ -1,9 +0,0 @@
|
|||||||
<project version="4">
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectType">
|
|
||||||
<option name="id" value="Android" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/app/src/main/cpp/libcxx" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
60
README.md
60
README.md
@ -1,31 +1,47 @@
|
|||||||
# Play Integrity Fix
|
# 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:
|
To use this module you must have one of this:
|
||||||
|
|
||||||
- Magisk with Zygisk enabled.
|
- Magisk with Zygisk enabled.
|
||||||
- KernelSU with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed.
|
- KernelSU with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed.
|
||||||
|
|
||||||
[**Download the latest here**](https://github.com/chiteroman/PlayIntegrityFix/releases/latest).
|
[**Download the latest here**](https://github.com/chiteroman/PlayIntegrityFix/releases/latest).
|
||||||
|
|
||||||
|
## Telegram group
|
||||||
|
|
||||||
|
https://t.me/playintegrityfix
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
- [PayPal](https://paypal.me/chiteroman)
|
- [PayPal](https://paypal.me/chiteroman)
|
||||||
|
|
||||||
## Official posts
|
## Official posts
|
||||||
|
|
||||||
- [XDA](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/)
|
- [XDA](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/)
|
||||||
|
|
||||||
## About module
|
## 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.
|
The purpose of the module is to avoid a hardware attestation.
|
||||||
|
|
||||||
## Failing BASIC verdict
|
## 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.
|
- Disable all modules except mine.
|
||||||
- Check your SELinux (must be enforced).
|
- Check your SELinux (must be enforced).
|
||||||
|
|
||||||
Some modules which modify system can trigger DroidGuard detection, never hook GMS processes.
|
Some modules which modify system can trigger DroidGuard detection, never hook GMS processes.
|
||||||
|
|
||||||
## Certify Play Store and fix Google Wallet
|
## Certify Play Store and fix Google Wallet
|
||||||
|
|
||||||
Follow this steps:
|
Follow this steps:
|
||||||
|
|
||||||
- Flash my module in Magisk/KernelSU (if you already have my module, just ignore this step).
|
- Flash my module in Magisk/KernelSU (if you already have my module, just ignore this step).
|
||||||
- Clear Google Wallet cache.
|
- Clear Google Wallet cache.
|
||||||
- Clear Google Play Store cache.
|
- Clear Google Play Store cache.
|
||||||
@ -35,44 +51,18 @@ Follow this steps:
|
|||||||
Reboot and done!
|
Reboot and done!
|
||||||
|
|
||||||
## Read module logs
|
## Read module logs
|
||||||
|
|
||||||
You can read module logs using this command:
|
You can read module logs using this command:
|
||||||
|
|
||||||
```
|
```
|
||||||
adb shell "logcat | grep 'PIF'"
|
adb shell "logcat | grep 'PIF'"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Can this module pass MEETS_STRONG_INTEGRITY?
|
## Can this module pass MEETS_STRONG_INTEGRITY?
|
||||||
**No**
|
|
||||||
|
|
||||||
## Play Integrity is now our problem, SafetyNet is deprecated
|
No.
|
||||||
You can read more info here: [click me](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/)
|
|
||||||
|
|
||||||
## Make FCM Push back to work after cleared GSF data
|
## About Play Integrity, SafetyNet is deprecated
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
You can read more info
|
||||||
|
here: [click me](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/)
|
||||||
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.
|
|
||||||
|
@ -19,7 +19,7 @@ android {
|
|||||||
ndk {
|
ndk {
|
||||||
//noinspection ChromeOsAbiSupport
|
//noinspection ChromeOsAbiSupport
|
||||||
abiFilters += setOf("armeabi-v7a", "arm64-v8a")
|
abiFilters += setOf("armeabi-v7a", "arm64-v8a")
|
||||||
jobs = 4
|
jobs = Runtime.getRuntime().availableProcessors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#define ZYGISK_API_VERSION 2
|
#define ZYGISK_API_VERSION 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -142,6 +142,7 @@ struct AppSpecializeArgs {
|
|||||||
jint &gid;
|
jint &gid;
|
||||||
jintArray &gids;
|
jintArray &gids;
|
||||||
jint &runtime_flags;
|
jint &runtime_flags;
|
||||||
|
jobjectArray &rlimits;
|
||||||
jint &mount_external;
|
jint &mount_external;
|
||||||
jstring &se_info;
|
jstring &se_info;
|
||||||
jstring &nice_name;
|
jstring &nice_name;
|
||||||
@ -149,6 +150,7 @@ struct AppSpecializeArgs {
|
|||||||
jstring &app_data_dir;
|
jstring &app_data_dir;
|
||||||
|
|
||||||
// Optional arguments. Please check whether the pointer is null before de-referencing
|
// 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_child_zygote;
|
||||||
jboolean *const is_top_app;
|
jboolean *const is_top_app;
|
||||||
jobjectArray *const pkg_data_info_list;
|
jobjectArray *const pkg_data_info_list;
|
||||||
@ -241,6 +243,14 @@ struct Api {
|
|||||||
// Returns bitwise-or'd zygisk::StateFlag values.
|
// Returns bitwise-or'd zygisk::StateFlag values.
|
||||||
uint32_t getFlags();
|
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
|
// Hook JNI native methods for a class
|
||||||
//
|
//
|
||||||
// Lookup all registered JNI native methods and replace it with your own methods.
|
// 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
|
// 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64
|
||||||
// (More details: https://man7.org/linux/man-pages/man5/proc.5.html)
|
// (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`.
|
// 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);
|
void pltHookRegister(dev_t dev, ino_t inode, 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);
|
|
||||||
|
|
||||||
// Commit all the hooks that was previously registered.
|
// Commit all the hooks that was previously registered.
|
||||||
// Returns false if an error occurred.
|
// Returns false if an error occurred.
|
||||||
@ -324,8 +331,8 @@ struct api_table {
|
|||||||
bool (*registerModule)(api_table *, module_abi *);
|
bool (*registerModule)(api_table *, module_abi *);
|
||||||
|
|
||||||
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||||
void (*pltHookRegister)(const char *, const char *, void *, void **);
|
void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **);
|
||||||
void (*pltHookExclude)(const char *, const char *);
|
bool (*exemptFd)(int);
|
||||||
bool (*pltHookCommit)();
|
bool (*pltHookCommit)();
|
||||||
int (*connectCompanion)(void * /* impl */);
|
int (*connectCompanion)(void * /* impl */);
|
||||||
void (*setOption)(void * /* impl */, Option);
|
void (*setOption)(void * /* impl */, Option);
|
||||||
@ -358,14 +365,14 @@ inline void Api::setOption(Option opt) {
|
|||||||
inline uint32_t Api::getFlags() {
|
inline uint32_t Api::getFlags() {
|
||||||
return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0;
|
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) {
|
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
|
||||||
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
||||||
}
|
}
|
||||||
inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
|
inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) {
|
||||||
if (tbl->pltHookRegister) tbl->pltHookRegister(regex, symbol, newFunc, oldFunc);
|
if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc);
|
||||||
}
|
|
||||||
inline void Api::pltHookExclude(const char *regex, const char *symbol) {
|
|
||||||
if (tbl->pltHookExclude) tbl->pltHookExclude(regex, symbol);
|
|
||||||
}
|
}
|
||||||
inline bool Api::pltHookCommit() {
|
inline bool Api::pltHookCommit() {
|
||||||
return tbl->pltHookCommit != nullptr && tbl->pltHookCommit();
|
return tbl->pltHookCommit != nullptr && tbl->pltHookCommit();
|
||||||
|
@ -60,10 +60,17 @@ public final class EntryPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void spoofDevice() {
|
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) {
|
private static void setProp(String name, String value) {
|
||||||
|
if (name == null || value == null || name.isEmpty() || value.isEmpty()) return;
|
||||||
try {
|
try {
|
||||||
Field field = Build.class.getDeclaredField(name);
|
Field field = Build.class.getDeclaredField(name);
|
||||||
field.setAccessible(true);
|
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) {
|
public static void LOG(String msg) {
|
||||||
Log.d("PIF/Java", msg);
|
Log.d("PIF/Java", msg);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.1.3" apply false
|
id("com.android.application") version "8.1.4" apply false
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user