You've already forked PlayIntegrityFork
mirror of
https://github.com/osm0sis/PlayIntegrityFork.git
synced 2025-09-06 06:37:06 +00:00
Merge remote-tracking branch 'upstream/custom' into upstream
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -13,3 +13,7 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
/module/classes.dex
|
||||||
|
/module/pif.json
|
||||||
|
/module/zygisk/*
|
||||||
|
/out/*
|
||||||
|
|||||||
10
.idea/deploymentTargetDropDown.xml
generated
Normal file
10
.idea/deploymentTargetDropDown.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetDropDown">
|
||||||
|
<value>
|
||||||
|
<entry key="app">
|
||||||
|
<State />
|
||||||
|
</entry>
|
||||||
|
</value>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/encodings.xml
generated
6
.idea/encodings.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding">
|
|
||||||
<file url="file://$PROJECT_DIR$/app/src/main/cpp/stlsoft/stlsoft/stlsoft.h" charset="windows-1252" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@@ -4,16 +4,15 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="testRunner" value="GRADLE" />
|
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="jbr-17" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
10
.idea/migrations.xml
generated
Normal file
10
.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/app/src/main/cpp/libcxx" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
58
README.md
58
README.md
@@ -1,9 +1,8 @@
|
|||||||
# Play Integrity Fix
|
# Play Integrity Fix
|
||||||
|
|
||||||
A Zygisk module which fix "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTEGRITY" (Play
|
A Zygisk module which fixes "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTEGRITY" (Play Integrity).
|
||||||
Integrity).
|
|
||||||
|
|
||||||
To use this module you must have one of this:
|
To use this module you must have one of the following:
|
||||||
|
|
||||||
- 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.
|
||||||
@@ -24,46 +23,41 @@ https://t.me/playintegrityfix
|
|||||||
|
|
||||||
## About module
|
## About module
|
||||||
|
|
||||||
It injects a classes.dex file to modify few fields in android.os.Build class. Also, in native code
|
It injects a classes.dex file to modify a few fields in the android.os.Build class. Also, it creates a hook in the native code to modify system properties. These are spoofed only to Google Play Services' DroidGuard (SafetyNet/Play Integrity) service.
|
||||||
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.
|
||||||
|
|
||||||
## About 'pif.json' file
|
## About 'custom.pif.json' file
|
||||||
|
|
||||||
You can modify this file to spoof android.os.Build fields in GMS unstable process and try to pass Device verdict.
|
You can create this file in the module directory to spoof custom values to the GMS unstable process. It will be used instead of the included pif.json.
|
||||||
You can't use values from recent devices due this devices must use a hardware attestation.
|
|
||||||
|
|
||||||
## Failing BASIC verdict
|
You can't use values from recent devices due them triggering hardware backed attestation.
|
||||||
|
|
||||||
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 (if you have it).
|
|
||||||
- Clear Google Play Store cache and data.
|
|
||||||
- Clear Google Play Services (com.google.android.gms) cache and data (Optionally skip clearing data and wait some time, ~24h, for it to resolve on its own).
|
|
||||||
- Reboot
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Fails to meet device integrity (KernelSU)
|
### Failing BASIC verdict
|
||||||
|
|
||||||
|
If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Integrity) something is wrong in your setup. Recommended steps in order to find the problem:
|
||||||
|
|
||||||
|
- Disable all modules except this one
|
||||||
|
|
||||||
|
Some modules which modify system can trigger DroidGuard detection, never hook GMS processes.
|
||||||
|
|
||||||
|
### Failing DEVICE verdict (on KernelSU)
|
||||||
|
|
||||||
- Disable ZygiskNext
|
- Disable ZygiskNext
|
||||||
- Reboot
|
- Reboot
|
||||||
- Enable ZygiskNext
|
- Enable ZygiskNext
|
||||||
|
|
||||||
### Passes device integrity, but fails in Wallet (even after clearing cache)
|
### Play Protect/Store Certification and Google Wallet Tap To Pay Setup Security Requirements
|
||||||
|
|
||||||
- Remove all data from Google Play Services
|
Follow these steps:
|
||||||
|
|
||||||
|
- Flash the module in Magisk/KernelSU
|
||||||
|
- Clear Google Wallet cache (if you have it)
|
||||||
|
- Clear Google Play Store cache and data
|
||||||
|
- Clear Google Play Services (com.google.android.gms) cache and data (Optionally skip clearing data and wait some time, ~24h, for it to resolve on its own)
|
||||||
|
- Reboot
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Guide</summary>
|
<summary>Guide</summary>
|
||||||
@@ -73,9 +67,9 @@ Follow this steps:
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Read module logs
|
### Read module logs
|
||||||
|
|
||||||
You can read module logs using this command:
|
You can read module logs using this command directly after boot:
|
||||||
|
|
||||||
```
|
```
|
||||||
adb shell "logcat | grep 'PIF'"
|
adb shell "logcat | grep 'PIF'"
|
||||||
|
|||||||
@@ -42,4 +42,33 @@ android {
|
|||||||
path = file("src/main/cpp/Android.mk")
|
path = file("src/main/cpp/Android.mk")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyFiles") {
|
||||||
|
doLast {
|
||||||
|
val moduleFolder = project.rootDir.resolve("module")
|
||||||
|
val dexFile = project.buildDir.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex")
|
||||||
|
val soDir = project.buildDir.resolve("intermediates/stripped_native_libs/release/out/lib")
|
||||||
|
|
||||||
|
dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
|
||||||
|
|
||||||
|
soDir.walk().filter { it.isFile && it.extension == "so" }.forEach { soFile ->
|
||||||
|
val abiFolder = soFile.parentFile.name
|
||||||
|
val destination = moduleFolder.resolve("zygisk/$abiFolder.so")
|
||||||
|
soFile.copyTo(destination, overwrite = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Zip>("zip") {
|
||||||
|
dependsOn("copyFiles")
|
||||||
|
|
||||||
|
archiveFileName.set("PlayIntegrityFix.zip")
|
||||||
|
destinationDirectory.set(project.rootDir.resolve("out"))
|
||||||
|
|
||||||
|
from(project.rootDir.resolve("module"))
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
tasks["assembleRelease"].finalizedBy("copyFiles", "zip")
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
APP_STL := none
|
APP_STL := none
|
||||||
APP_CFLAGS := -Oz -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden
|
APP_CFLAGS := -Oz -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden
|
||||||
APP_CPPFLAGS := -std=c++20
|
APP_CPPFLAGS := -std=c++20
|
||||||
APP_LDFLAGS := -Oz
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <sys/system_properties.h>
|
#include <sys/system_properties.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "zygisk.hpp"
|
#include "zygisk.hpp"
|
||||||
#include "shadowhook.h"
|
#include "shadowhook.h"
|
||||||
@@ -11,7 +10,9 @@
|
|||||||
|
|
||||||
#define DEX_FILE_PATH "/data/adb/modules/playintegrityfix/classes.dex"
|
#define DEX_FILE_PATH "/data/adb/modules/playintegrityfix/classes.dex"
|
||||||
|
|
||||||
#define PROP_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"
|
||||||
|
|
||||||
static std::string FIRST_API_LEVEL, SECURITY_PATCH;
|
static std::string FIRST_API_LEVEL, SECURITY_PATCH;
|
||||||
|
|
||||||
@@ -28,15 +29,17 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
|
|||||||
std::string_view prop(name);
|
std::string_view prop(name);
|
||||||
|
|
||||||
if (prop.ends_with("api_level")) {
|
if (prop.ends_with("api_level")) {
|
||||||
if (FIRST_API_LEVEL == "NULL") {
|
if (FIRST_API_LEVEL.empty()) {
|
||||||
|
LOGD("FIRST_API_LEVEL is empty, ignoring it...");
|
||||||
|
} else if (FIRST_API_LEVEL == "nullptr") {
|
||||||
value = nullptr;
|
value = nullptr;
|
||||||
} else {
|
} else {
|
||||||
value = FIRST_API_LEVEL.c_str();
|
value = FIRST_API_LEVEL.c_str();
|
||||||
}
|
}
|
||||||
LOGD("[%s] -> %s", name, value);
|
LOGD("[%s] -> %s", name, value);
|
||||||
} else if (prop.ends_with("security_patch")) {
|
} else if (prop.ends_with("security_patch")) {
|
||||||
if (SECURITY_PATCH == "NULL") {
|
if (SECURITY_PATCH.empty()) {
|
||||||
value = nullptr;
|
LOGD("SECURITY_PATCH is empty, ignoring it...");
|
||||||
} else {
|
} else {
|
||||||
value = SECURITY_PATCH.c_str();
|
value = SECURITY_PATCH.c_str();
|
||||||
}
|
}
|
||||||
@@ -72,27 +75,6 @@ static void doHook() {
|
|||||||
LOGD("Found '__system_property_read_callback' handle at %p", handle);
|
LOGD("Found '__system_property_read_callback' handle at %p", handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendVector(int fd, const std::vector<char> &vec) {
|
|
||||||
// Send the size of the vector
|
|
||||||
size_t size = vec.size();
|
|
||||||
write(fd, &size, sizeof(size_t));
|
|
||||||
|
|
||||||
// Send the vector data
|
|
||||||
write(fd, vec.data(), size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<char> receiveVector(int fd) {
|
|
||||||
// Receive the size of the vector
|
|
||||||
size_t size;
|
|
||||||
read(fd, &size, sizeof(size_t));
|
|
||||||
|
|
||||||
// Receive the vector data
|
|
||||||
std::vector<char> vec(size);
|
|
||||||
read(fd, vec.data(), size);
|
|
||||||
|
|
||||||
return vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlayIntegrityFix : public zygisk::ModuleBase {
|
class PlayIntegrityFix : public zygisk::ModuleBase {
|
||||||
public:
|
public:
|
||||||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||||||
@@ -101,15 +83,17 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||||||
bool isGms = false;
|
bool isGms = false, isGmsUnstable = false;
|
||||||
bool isGmsUnstable = false;
|
|
||||||
|
|
||||||
auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
|
auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
|
||||||
|
|
||||||
if (rawProcess) {
|
if (rawProcess) {
|
||||||
std::string_view process(rawProcess);
|
std::string_view process(rawProcess);
|
||||||
|
|
||||||
isGms = process.starts_with("com.google.android.gms");
|
isGms = process.starts_with("com.google.android.gms");
|
||||||
isGmsUnstable = process.compare("com.google.android.gms.unstable") == 0;
|
isGmsUnstable = process.compare("com.google.android.gms.unstable") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
|
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
|
||||||
|
|
||||||
if (!isGms) {
|
if (!isGms) {
|
||||||
@@ -124,32 +108,55 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long dexSize = 0, jsonSize = 0;
|
||||||
int fd = api->connectCompanion();
|
int fd = api->connectCompanion();
|
||||||
|
|
||||||
dexVector = receiveVector(fd);
|
read(fd, &dexSize, sizeof(long));
|
||||||
propVector = receiveVector(fd);
|
read(fd, &jsonSize, sizeof(long));
|
||||||
|
|
||||||
|
if (dexSize < 1) {
|
||||||
|
close(fd);
|
||||||
|
LOGD("Couldn't read classes.dex");
|
||||||
|
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonSize < 1) {
|
||||||
|
close(fd);
|
||||||
|
LOGD("Couldn't read pif.json");
|
||||||
|
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dexVector.resize(dexSize);
|
||||||
|
read(fd, dexVector.data(), dexSize);
|
||||||
|
|
||||||
|
std::vector<char> jsonVector(jsonSize);
|
||||||
|
read(fd, jsonVector.data(), jsonSize);
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
LOGD("Read from file descriptor file 'classes.dex' -> %d bytes",
|
LOGD("Read from file descriptor file 'classes.dex' -> %ld bytes", dexSize);
|
||||||
static_cast<int>(dexVector.size()));
|
LOGD("Read from file descriptor file 'pif.json' -> %ld bytes", jsonSize);
|
||||||
LOGD("Read from file descriptor file 'pif.json' -> %d bytes",
|
|
||||||
static_cast<int>(propVector.size()));
|
|
||||||
|
|
||||||
if (dexVector.empty() || propVector.empty()) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
std::string data(jsonVector.cbegin(), jsonVector.cend());
|
||||||
|
json = nlohmann::json::parse(data, nullptr, false, true);
|
||||||
|
|
||||||
|
jsonVector.clear();
|
||||||
|
data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
||||||
if (dexVector.empty() || propVector.empty()) return;
|
if (dexVector.empty() || json.empty()) return;
|
||||||
|
|
||||||
readJson();
|
readJson();
|
||||||
|
|
||||||
inject();
|
|
||||||
|
|
||||||
doHook();
|
doHook();
|
||||||
|
|
||||||
|
inject();
|
||||||
|
|
||||||
dexVector.clear();
|
dexVector.clear();
|
||||||
propVector.clear();
|
json.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override {
|
void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override {
|
||||||
@@ -159,19 +166,35 @@ public:
|
|||||||
private:
|
private:
|
||||||
zygisk::Api *api = nullptr;
|
zygisk::Api *api = nullptr;
|
||||||
JNIEnv *env = nullptr;
|
JNIEnv *env = nullptr;
|
||||||
std::vector<char> dexVector, propVector;
|
std::vector<char> dexVector;
|
||||||
|
nlohmann::json json;
|
||||||
|
|
||||||
void readJson() {
|
void readJson() {
|
||||||
std::string data(propVector.cbegin(), propVector.cend());
|
if (json.contains("SECURITY_PATCH")) {
|
||||||
nlohmann::json json = nlohmann::json::parse(data, nullptr, false, true);
|
if (json["SECURITY_PATCH"].is_null()) {
|
||||||
|
LOGD("Key SECURITY_PATCH is null!");
|
||||||
|
} else if (json["SECURITY_PATCH"].is_string()) {
|
||||||
|
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
|
||||||
|
} else {
|
||||||
|
LOGD("Error parsing SECURITY_PATCH!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGD("Key SECURITY_PATCH doesn't exist in JSON file!");
|
||||||
|
}
|
||||||
|
|
||||||
auto getStringFromJson = [&json](const std::string &key) {
|
if (json.contains("FIRST_API_LEVEL")) {
|
||||||
return json.contains(key) && !json[key].is_null() ? json[key].get<std::string>()
|
if (json["FIRST_API_LEVEL"].is_null()) {
|
||||||
: "NULL";
|
LOGD("Key FIRST_API_LEVEL is null!");
|
||||||
};
|
FIRST_API_LEVEL = "nullptr";
|
||||||
|
} else if (json["FIRST_API_LEVEL"].is_string()) {
|
||||||
SECURITY_PATCH = getStringFromJson("SECURITY_PATCH");
|
FIRST_API_LEVEL = json["FIRST_API_LEVEL"].get<std::string>();
|
||||||
FIRST_API_LEVEL = getStringFromJson("FIRST_API_LEVEL");
|
} else {
|
||||||
|
LOGD("Error parsing FIRST_API_LEVEL!");
|
||||||
|
}
|
||||||
|
json.erase("FIRST_API_LEVEL");
|
||||||
|
} else {
|
||||||
|
LOGD("Key FIRST_API_LEVEL doesn't exist in JSON file!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void inject() {
|
void inject() {
|
||||||
@@ -200,8 +223,7 @@ private:
|
|||||||
LOGD("read json");
|
LOGD("read json");
|
||||||
auto readProps = env->GetStaticMethodID(entryClass, "readJson",
|
auto readProps = env->GetStaticMethodID(entryClass, "readJson",
|
||||||
"(Ljava/lang/String;)V");
|
"(Ljava/lang/String;)V");
|
||||||
std::string data(propVector.cbegin(), propVector.cend());
|
auto javaStr = env->NewStringUTF(json.dump().c_str());
|
||||||
auto javaStr = env->NewStringUTF(data.c_str());
|
|
||||||
env->CallStaticVoidMethod(entryClass, readProps, javaStr);
|
env->CallStaticVoidMethod(entryClass, readProps, javaStr);
|
||||||
|
|
||||||
LOGD("call init");
|
LOGD("call init");
|
||||||
@@ -211,22 +233,45 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void companion(int fd) {
|
static void companion(int fd) {
|
||||||
std::ifstream dex(DEX_FILE_PATH, std::ios::binary);
|
long dexSize = 0, jsonSize = 0;
|
||||||
std::ifstream prop(PROP_FILE_PATH);
|
std::vector<char> dexVector, jsonVector;
|
||||||
|
|
||||||
std::vector<char> dexVector((std::istreambuf_iterator<char>(dex)),
|
FILE *dex = fopen(DEX_FILE_PATH, "rb");
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
std::vector<char> propVector((std::istreambuf_iterator<char>(prop)),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
|
|
||||||
dex.close();
|
if (dex) {
|
||||||
prop.close();
|
fseek(dex, 0, SEEK_END);
|
||||||
|
dexSize = ftell(dex);
|
||||||
|
fseek(dex, 0, SEEK_SET);
|
||||||
|
|
||||||
sendVector(fd, dexVector);
|
dexVector.resize(dexSize);
|
||||||
sendVector(fd, propVector);
|
fread(dexVector.data(), 1, dexSize, dex);
|
||||||
|
|
||||||
|
fclose(dex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *json = fopen(CUSTOM_JSON_FILE_PATH, "r");
|
||||||
|
if (!json)
|
||||||
|
FILE *json = fopen(JSON_FILE_PATH, "r");
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
fseek(json, 0, SEEK_END);
|
||||||
|
jsonSize = ftell(json);
|
||||||
|
fseek(json, 0, SEEK_SET);
|
||||||
|
|
||||||
|
jsonVector.resize(jsonSize);
|
||||||
|
fread(jsonVector.data(), 1, jsonSize, json);
|
||||||
|
|
||||||
|
fclose(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
write(fd, &dexSize, sizeof(long));
|
||||||
|
write(fd, &jsonSize, sizeof(long));
|
||||||
|
|
||||||
|
write(fd, dexVector.data(), dexSize);
|
||||||
|
write(fd, jsonVector.data(), jsonSize);
|
||||||
|
|
||||||
dexVector.clear();
|
dexVector.clear();
|
||||||
propVector.clear();
|
jsonVector.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_ZYGISK_MODULE(PlayIntegrityFix)
|
REGISTER_ZYGISK_MODULE(PlayIntegrityFix)
|
||||||
|
|||||||
17
changelog.md
17
changelog.md
@@ -2,15 +2,12 @@ We have a Telegram channel!
|
|||||||
If you want to share your knowledge join:
|
If you want to share your knowledge join:
|
||||||
https://t.me/playintegrityfix
|
https://t.me/playintegrityfix
|
||||||
|
|
||||||
# v13.7
|
# CUSTOM SPOOF v2.1
|
||||||
|
|
||||||
|
DO NOT USE THIS BUILD IF YOU AREN'T A DEVELOPER, ALWAYS USE LATEST STABLE.
|
||||||
|
THIS BUILD IS JUST FOR TESTING PURPOSES.
|
||||||
|
|
||||||
- Removed custom resetprop.
|
- Removed custom resetprop.
|
||||||
- Removed custom props spoof.
|
- Fix JSON parsing.
|
||||||
- Removed weird code.
|
- Less libs size.
|
||||||
|
- Few code improvements.
|
||||||
If you want an undetectable resetprop, use Kitsune Magisk.
|
|
||||||
If you want to spoof your own props, modify the props in source code and build by yourself.
|
|
||||||
Or you can use unstable build, you can download in GitHub repo.
|
|
||||||
Should work and don't crash nothing.
|
|
||||||
|
|
||||||
I recommend to clear GMS data and cache before reboot.
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#MAGISK
|
#MAGISK
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,14 +1,23 @@
|
|||||||
# Error on < Android 8
|
# Error on < Android 8
|
||||||
if [ "$API" -lt 26 ]; then
|
if [ "$API" -lt 26 ]; then
|
||||||
abort "!!! You can't use this module on Android < 8.0."
|
abort "!!! You can't use this module on Android < 8.0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# safetynet-fix module is incompatible
|
# Remove/warn if conflicting modules are installed
|
||||||
if [ -d "/data/adb/modules/safetynet-fix" ]; then
|
if [ -d /data/adb/modules/safetynet-fix ]; then
|
||||||
touch "/data/adb/modules/safetynet-fix/remove"
|
touch /data/adb/modules/safetynet-fix/remove
|
||||||
ui_print "!!! safetynet-fix module removed!"
|
ui_print "- 'safetynet-fix' module will be removed on next reboot"
|
||||||
|
fi
|
||||||
|
if [ -d /data/adb/modules/MagiskHidePropsConf ]; then
|
||||||
|
ui_print "- Warning, 'MagiskHidePropsConf' module may cause issues with PIF"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# use our resetprop
|
# Copy any custom.pif.json to updated module
|
||||||
mv -f "$MODPATH/bin/$ABI/resetprop" "$MODPATH"
|
if [ -f /data/adb/modules/playintegrityfix/custom.pif.json ]; then
|
||||||
rm -rf "$MODPATH/bin"
|
ui_print "- Restoring custom.pif.json"
|
||||||
|
cp -af /data/adb/modules/playintegrityfix/custom.pif.json $MODPATH/custom.pif.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up any leftover files from previous deprecated methods
|
||||||
|
rm -f /data/data/com.google.android.gms/cache/pif.prop /data/data/com.google.android.gms/pif.prop
|
||||||
|
rm -f /data/data/com.google.android.gms/cache/pif.json /data/data/com.google.android.gms/pif.json
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
id=playintegrityfix
|
id=playintegrityfix
|
||||||
name=Play Integrity Fix
|
name=Play Integrity Fix
|
||||||
version=v1.2-PROPS
|
version=PROPS-v2.1
|
||||||
versionCode=12
|
versionCode=2101
|
||||||
author=chiteroman
|
author=chiteroman
|
||||||
description=Fix CTS profile (SafetyNet) and DEVICE verdict (Play Integrity).
|
description=Fix CTS profile (SafetyNet) and DEVICE verdict (Play Integrity).
|
||||||
|
updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/custom/update.json
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"PRODUCT": "taimen",
|
|
||||||
"DEVICE": "taimen",
|
|
||||||
"MANUFACTURER": "Google",
|
|
||||||
"BRAND": "google",
|
|
||||||
"MODEL": "Pixel 2 XL",
|
|
||||||
"FINGERPRINT": "google/taimen/taimen:8.1.0/OPM4.171019.021.R1/4833808:user/release-keys",
|
|
||||||
"SECURITY_PATCH": "2018-07-05",
|
|
||||||
"FIRST_API_LEVEL": "25"
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,43 @@
|
|||||||
# Remove Play Services from the Magisk Denylist when set to enforcing
|
# Remove Play Services from Magisk Denylist when set to enforcing
|
||||||
if magisk --denylist status; then
|
if magisk --denylist status; then
|
||||||
magisk --denylist rm com.google.android.gms
|
magisk --denylist rm com.google.android.gms
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if safetynet-fix is installed
|
# Remove conflicting modules if installed
|
||||||
if [ -d "/data/adb/modules/safetynet-fix" ]; then
|
if [ -d /data/adb/modules/safetynet-fix ]; then
|
||||||
touch "/data/adb/modules/safetynet-fix/remove"
|
touch /data/adb/modules/safetynet-fix/remove
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Conditional early sensitive properties
|
||||||
|
|
||||||
|
resetprop_if_diff() {
|
||||||
|
local NAME=$1
|
||||||
|
local EXPECTED=$2
|
||||||
|
local CURRENT=$(resetprop $NAME)
|
||||||
|
|
||||||
|
[ -z "$CURRENT" ] || [ "$CURRENT" == "$EXPECTED" ] || resetprop $NAME $EXPECTED
|
||||||
|
}
|
||||||
|
resetprop_if_match() {
|
||||||
|
local NAME=$1
|
||||||
|
local CONTAINS=$2
|
||||||
|
local VALUE=$3
|
||||||
|
|
||||||
|
[[ "$(resetprop $NAME)" == *"$CONTAINS"* ]] && resetprop $NAME $VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
# RootBeer, Microsoft
|
||||||
|
resetprop_if_diff ro.build.tags release-keys
|
||||||
|
|
||||||
|
# Samsung
|
||||||
|
resetprop_if_diff ro.boot.warranty_bit 0
|
||||||
|
resetprop_if_diff ro.vendor.boot.warranty_bit 0
|
||||||
|
resetprop_if_diff ro.vendor.warranty_bit 0
|
||||||
|
resetprop_if_diff ro.warranty_bit 0
|
||||||
|
|
||||||
|
# OnePlus
|
||||||
|
resetprop_if_diff ro.is_ever_orange 0
|
||||||
|
|
||||||
|
# Other
|
||||||
|
resetprop_if_diff ro.build.type user
|
||||||
|
resetprop_if_diff ro.debuggable 0
|
||||||
|
resetprop_if_diff ro.secure 1
|
||||||
|
|||||||
@@ -1,76 +1,53 @@
|
|||||||
# Sensitive properties
|
# Conditional sensitive properties
|
||||||
|
|
||||||
RESETPROP="${0%/*}/resetprop"
|
resetprop_if_diff() {
|
||||||
|
local NAME=$1
|
||||||
|
local EXPECTED=$2
|
||||||
|
local CURRENT=$(resetprop $NAME)
|
||||||
|
|
||||||
chmod 755 $RESETPROP
|
[ -z "$CURRENT" ] || [ "$CURRENT" == "$EXPECTED" ] || resetprop $NAME $EXPECTED
|
||||||
|
|
||||||
check_resetprop() {
|
|
||||||
local NAME=$1
|
|
||||||
local EXPECTED=$2
|
|
||||||
local VALUE=$(resetprop $NAME)
|
|
||||||
[ -z $VALUE ] || [ $VALUE = $EXPECTED ] || $RESETPROP -n $NAME $EXPECTED
|
|
||||||
}
|
}
|
||||||
|
resetprop_if_match() {
|
||||||
|
local NAME=$1
|
||||||
|
local CONTAINS=$2
|
||||||
|
local VALUE=$3
|
||||||
|
|
||||||
maybe_set_prop() {
|
[[ "$(resetprop $NAME)" == *"$CONTAINS"* ]] && resetprop $NAME $VALUE
|
||||||
local prop="$1"
|
|
||||||
local contains="$2"
|
|
||||||
local value="$3"
|
|
||||||
|
|
||||||
if [[ "$(getprop "$prop")" == *"$contains"* ]]; then
|
|
||||||
$RESETPROP -n "$prop" "$value"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Magisk recovery mode
|
# Magisk recovery mode
|
||||||
maybe_set_prop ro.bootmode recovery unknown
|
resetprop_if_match ro.bootmode recovery unknown
|
||||||
maybe_set_prop ro.boot.mode recovery unknown
|
resetprop_if_match ro.boot.mode recovery unknown
|
||||||
maybe_set_prop vendor.boot.mode recovery unknown
|
resetprop_if_match vendor.boot.mode recovery unknown
|
||||||
|
|
||||||
# Hiding SELinux | Permissive status
|
# SELinux
|
||||||
if [ -n "$(getprop ro.build.selinux)" ]; then
|
if [ -n "$(resetprop ro.build.selinux)" ]; then
|
||||||
resetprop --delete ro.build.selinux
|
resetprop --delete ro.build.selinux
|
||||||
fi
|
fi
|
||||||
|
# use toybox to protect *stat* access time reading
|
||||||
# Hiding SELinux | Use toybox to protect *stat* access time reading
|
if [ "$(toybox cat /sys/fs/selinux/enforce)" == "0" ]; then
|
||||||
if [[ "$(toybox cat /sys/fs/selinux/enforce)" == "0" ]]; then
|
|
||||||
chmod 640 /sys/fs/selinux/enforce
|
chmod 640 /sys/fs/selinux/enforce
|
||||||
chmod 440 /sys/fs/selinux/policy
|
chmod 440 /sys/fs/selinux/policy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Reset props after boot completed to avoid breaking some weird devices/ROMs...
|
# SafetyNet/Play Integrity
|
||||||
{
|
{
|
||||||
until [[ "$(getprop sys.boot_completed)" == "1" ]]; do
|
# late props which must be set after boot_completed for various OEMs
|
||||||
|
until [ "$(getprop sys.boot_completed)" == "1" ]; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
# SafetyNet/Play Integrity | Avoid breaking Realme fingerprint scanners
|
# Avoid breaking Realme fingerprint scanners
|
||||||
check_resetprop ro.boot.flash.locked 1
|
resetprop_if_diff ro.boot.flash.locked 1
|
||||||
|
|
||||||
# SafetyNet/Play Integrity | Avoid breaking Oppo fingerprint scanners
|
# Avoid breaking Oppo fingerprint scanners
|
||||||
check_resetprop ro.boot.vbmeta.device_state locked
|
resetprop_if_diff ro.boot.vbmeta.device_state locked
|
||||||
|
|
||||||
# SafetyNet/Play Integrity | Avoid breaking OnePlus display modes/fingerprint scanners
|
# Avoid breaking OnePlus display modes/fingerprint scanners
|
||||||
check_resetprop vendor.boot.verifiedbootstate green
|
resetprop_if_diff vendor.boot.verifiedbootstate green
|
||||||
|
|
||||||
# SafetyNet/Play Integrity | Avoid breaking OnePlus display modes/fingerprint scanners on OOS 12
|
# Avoid breaking OnePlus/Oppo display fingerprint scanners on OOS/ColorOS 12+
|
||||||
check_resetprop ro.boot.verifiedbootstate green
|
resetprop_if_diff ro.boot.verifiedbootstate green
|
||||||
check_resetprop ro.boot.veritymode enforcing
|
resetprop_if_diff ro.boot.veritymode enforcing
|
||||||
check_resetprop vendor.boot.vbmeta.device_state locked
|
resetprop_if_diff vendor.boot.vbmeta.device_state locked
|
||||||
|
}&
|
||||||
# RootBeer, Microsoft
|
|
||||||
check_resetprop ro.build.tags release-keys
|
|
||||||
|
|
||||||
# Samsung
|
|
||||||
check_resetprop ro.boot.warranty_bit 0
|
|
||||||
check_resetprop ro.vendor.boot.warranty_bit 0
|
|
||||||
check_resetprop ro.vendor.warranty_bit 0
|
|
||||||
check_resetprop ro.warranty_bit 0
|
|
||||||
|
|
||||||
# OnePlus
|
|
||||||
check_resetprop ro.is_ever_orange 0
|
|
||||||
|
|
||||||
# Other
|
|
||||||
check_resetprop ro.build.type user
|
|
||||||
check_resetprop ro.debuggable 0
|
|
||||||
check_resetprop ro.secure 1
|
|
||||||
}&
|
|
||||||
|
|||||||
10
update.json
10
update.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "v13.7",
|
"version": "v2.1",
|
||||||
"versionCode": 137,
|
"versionCode": 2101,
|
||||||
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v13.7/PlayIntegrityFix_v13.7.zip",
|
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/PROPS/PlayIntegrityFix_PROPS-v2.1.zip",
|
||||||
"changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md"
|
"changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/custom/changelog.md"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user