mirror of
https://github.com/chiteroman/PlayIntegrityFix.git
synced 2025-01-19 03:22:39 +02:00
v15.9.5
This commit is contained in:
parent
8e239aade9
commit
972bf318cb
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
|
47
README.md
47
README.md
@ -1,15 +1,46 @@
|
||||
# Play Integrity Fix
|
||||
|
||||
This module attempts to fix Play Integrity verdicts to get a certified device on bootloader unlocked devices.
|
||||
This module tries to fix Play Integrity and SafetyNet verdicts to get a valid attestation.
|
||||
|
||||
Device verdict should pass by default.
|
||||
If not, try removing /data/adb/pif.json file.
|
||||
DO NOT REMOVE pif.json in module's folder!
|
||||
You will need root and Zygisk, so you must choose ONE of this three setups:
|
||||
|
||||
Wiki: https://github.com/chiteroman/PlayIntegrityFix/wiki
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) with Zygisk enabled.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed.
|
||||
- [APatch](https://github.com/bmax121/APatch) with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed.
|
||||
|
||||
XDA post: https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/
|
||||
After flashing and reboot your device, you can check PI and SN using these apps:
|
||||
|
||||
Telegram group: https://t.me/playintegrityfix
|
||||
- Play Integrity -> https://play.google.com/store/apps/details?id=gr.nikolasspyr.integritycheck
|
||||
- SafetyNet -> https://play.google.com/store/apps/details?id=rikka.safetynetchecker
|
||||
|
||||
Donations: https://www.paypal.com/paypalme/chiteroman
|
||||
NOTE: if you get an error message about a limit, you need to use another app, this is because a lot of users are requesting an attestation.
|
||||
|
||||
NOTE: SafetyNet is obsolete, more info here: https://developer.android.com/privacy-and-security/safetynet/deprecation-timeline
|
||||
|
||||
Also, if you are using custom rom or custom kernel, be sure that your kernel name isn't blacklisted, you can check it running ```uname -r``` command. This is a list of banned strings: https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89308909
|
||||
|
||||
After requesting an attestation in Play Integrity API you should get this result:
|
||||
|
||||
- MEETS_BASIC_INTEGRITY ✅
|
||||
- MEETS_DEVICE_INTEGRITY ✅
|
||||
- MEETS_STRONG_INTEGRITY ❌
|
||||
- MEETS_VIRTUAL_INTEGRITY ❌
|
||||
|
||||
You can know more about verdicts in this post: https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/
|
||||
|
||||
And in SafetyNet you should get this:
|
||||
|
||||
- basicIntegrity: true
|
||||
- ctsProfileMatch: true
|
||||
- evaluationType: BASIC
|
||||
|
||||
NOTE: Strong verdict is impossible to pass on unlocked bootloader devices, there are few devices and "exploits" which will allow you to pass it, but, in normal conditions, this verdict will be green only if you are using stock ROM and locked bootloader. The old posts talking about Strong pass was an "exploit" in Google servers, obviously, now it's patched.
|
||||
|
||||
FAQ: https://xdaforums.com/t/pif-faq.4653307/
|
||||
|
||||
## Download
|
||||
https://github.com/chiteroman/PlayIntegrityFix/releases/latest
|
||||
|
||||
## Donations
|
||||
|
||||
[PayPal](https://www.paypal.com/paypalme/chiteroman)
|
@ -12,10 +12,14 @@ android {
|
||||
applicationId = "es.chiteroman.playintegrityfix"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 15940
|
||||
versionName = "v15.9.4"
|
||||
versionCode = 15950
|
||||
versionName = "v15.9.5"
|
||||
multiDexEnabled = false
|
||||
|
||||
buildFeatures {
|
||||
prefab = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
excludes += "**/liblog.so"
|
||||
@ -25,8 +29,8 @@ android {
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments += "-DANDROID_STL=c++_static"
|
||||
arguments += "-DCMAKE_BUILD_TYPE=Release"
|
||||
arguments += "-DANDROID_STL=none"
|
||||
arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
|
||||
arguments += "-DPlugin.Android.BionicLinkerUtil=ON"
|
||||
|
||||
cppFlags += "-std=c++20"
|
||||
@ -34,7 +38,6 @@ android {
|
||||
cppFlags += "-fno-rtti"
|
||||
cppFlags += "-fvisibility=hidden"
|
||||
cppFlags += "-fvisibility-inlines-hidden"
|
||||
cppFlags += "-flto"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +64,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
|
||||
}
|
||||
|
||||
tasks.register("updateModuleProp") {
|
||||
doLast {
|
||||
val versionName = project.android.defaultConfig.versionName
|
||||
@ -84,7 +91,7 @@ tasks.register("copyFiles") {
|
||||
doLast {
|
||||
val moduleFolder = project.rootDir.resolve("module")
|
||||
val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex")
|
||||
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/out/lib")
|
||||
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/stripReleaseDebugSymbols/out/lib")
|
||||
|
||||
dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
|
||||
|
||||
|
@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project(playintegrityfix)
|
||||
|
||||
find_package(cxx REQUIRED CONFIG)
|
||||
|
||||
link_libraries(cxx::cxx)
|
||||
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp)
|
||||
|
||||
add_subdirectory(Dobby)
|
||||
|
@ -17,11 +17,13 @@ static std::string FIRST_API_LEVEL, SECURITY_PATCH, BUILD_ID;
|
||||
|
||||
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
|
||||
|
||||
static T_Callback o_callback = nullptr;
|
||||
static std::map<void *, T_Callback> callbacks;
|
||||
|
||||
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
|
||||
|
||||
if (cookie == nullptr || name == nullptr || value == nullptr || o_callback == nullptr) return;
|
||||
if (cookie == nullptr || name == nullptr || value == nullptr ||
|
||||
!callbacks.contains(cookie))
|
||||
return;
|
||||
|
||||
std::string_view prop(name);
|
||||
|
||||
@ -52,7 +54,7 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
|
||||
LOGD("[%s]: %s", name, value);
|
||||
}
|
||||
|
||||
return o_callback(cookie, name, value, serial);
|
||||
return callbacks[cookie](cookie, name, value, serial);
|
||||
}
|
||||
|
||||
static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *);
|
||||
@ -62,19 +64,19 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void
|
||||
if (pi == nullptr || callback == nullptr || cookie == nullptr) {
|
||||
return o_system_property_read_callback(pi, callback, cookie);
|
||||
}
|
||||
o_callback = callback;
|
||||
callbacks[cookie] = callback;
|
||||
return o_system_property_read_callback(pi, modify_callback, cookie);
|
||||
}
|
||||
|
||||
static void doHook() {
|
||||
void *handle = DobbySymbolResolver("libc.so", "__system_property_read_callback");
|
||||
void *handle = DobbySymbolResolver(nullptr, "__system_property_read_callback");
|
||||
if (handle == nullptr) {
|
||||
LOGD("Couldn't hook '__system_property_read_callback'. Report to @chiteroman");
|
||||
LOGD("Couldn't hook __system_property_read_callback");
|
||||
return;
|
||||
}
|
||||
DobbyHook(handle, (void *) my_system_property_read_callback,
|
||||
(void **) &o_system_property_read_callback);
|
||||
LOGD("Found and hooked '__system_property_read_callback' at %p", handle);
|
||||
LOGD("Found and hooked __system_property_read_callback at %p", handle);
|
||||
}
|
||||
|
||||
class PlayIntegrityFix : public zygisk::ModuleBase {
|
||||
@ -86,77 +88,71 @@ public:
|
||||
|
||||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||||
|
||||
auto dir = env->GetStringUTFChars(args->app_data_dir, nullptr);
|
||||
const char *dir, *name;
|
||||
bool isGms, isGmsUnstable;
|
||||
|
||||
if (dir == nullptr) {
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
if (!args) goto exit;
|
||||
|
||||
bool isGms = std::string_view(dir).ends_with("/com.google.android.gms");
|
||||
dir = env->GetStringUTFChars(args->app_data_dir, nullptr);
|
||||
|
||||
if (!dir) goto exit;
|
||||
|
||||
isGms = std::string_view(dir).ends_with("/com.google.android.gms");
|
||||
|
||||
env->ReleaseStringUTFChars(args->app_data_dir, dir);
|
||||
|
||||
if (!isGms) {
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
if (isGms) {
|
||||
name = env->GetStringUTFChars(args->nice_name, nullptr);
|
||||
|
||||
if (!name) goto exit;
|
||||
|
||||
isGmsUnstable = strcmp(name, "com.google.android.gms.unstable") == 0;
|
||||
|
||||
if (isGmsUnstable) {
|
||||
|
||||
long dexSize = 0, jsonSize = 0;
|
||||
|
||||
int fd = api->connectCompanion();
|
||||
|
||||
read(fd, &dexSize, sizeof(long));
|
||||
read(fd, &jsonSize, sizeof(long));
|
||||
|
||||
LOGD("Dex file size: %ld", dexSize);
|
||||
LOGD("Json file size: %ld", jsonSize);
|
||||
|
||||
if (dexSize < 1 || jsonSize < 1) {
|
||||
close(fd);
|
||||
LOGD("Invalid files!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dexVector.resize(dexSize);
|
||||
read(fd, dexVector.data(), dexSize);
|
||||
|
||||
std::vector<uint8_t> jsonVector;
|
||||
|
||||
jsonVector.resize(jsonSize);
|
||||
read(fd, jsonVector.data(), jsonSize);
|
||||
|
||||
close(fd);
|
||||
|
||||
json = nlohmann::json::parse(jsonVector, nullptr, false, true);
|
||||
|
||||
parseJson();
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
} else {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
|
||||
|
||||
auto name = env->GetStringUTFChars(args->nice_name, nullptr);
|
||||
|
||||
if (name == nullptr) {
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isGmsUnstable = std::string_view(name) == "com.google.android.gms.unstable";
|
||||
|
||||
env->ReleaseStringUTFChars(args->nice_name, name);
|
||||
|
||||
if (!isGmsUnstable) {
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
long dexSize = 0, jsonSize = 0;
|
||||
|
||||
int fd = api->connectCompanion();
|
||||
|
||||
read(fd, &dexSize, sizeof(long));
|
||||
read(fd, &jsonSize, sizeof(long));
|
||||
|
||||
LOGD("Dex file size: %ld", dexSize);
|
||||
LOGD("Json file size: %ld", jsonSize);
|
||||
|
||||
if (dexSize < 1) {
|
||||
close(fd);
|
||||
LOGD("Dex file empty!");
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
dexVector.resize(dexSize);
|
||||
read(fd, dexVector.data(), dexSize);
|
||||
|
||||
if (jsonSize < 1) {
|
||||
close(fd);
|
||||
LOGD("JSON file not found!");
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> jsonVector;
|
||||
|
||||
jsonVector.resize(jsonSize);
|
||||
read(fd, jsonVector.data(), jsonSize);
|
||||
|
||||
close(fd);
|
||||
|
||||
json = nlohmann::json::parse(jsonVector, nullptr, false, true);
|
||||
|
||||
parseJson();
|
||||
exit:
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
}
|
||||
|
||||
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
||||
|
@ -10,15 +10,12 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class CustomKeyStoreSpi extends KeyStoreSpi {
|
||||
private static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25";
|
||||
private static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17";
|
||||
private static final String KNOX_OID = "1.3.6.1.4.1.236.11.3.23.7";
|
||||
public static volatile KeyStoreSpi keyStoreSpi = null;
|
||||
public static volatile KeyStoreSpi keyStoreSpi;
|
||||
|
||||
@Override
|
||||
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
|
||||
@ -27,24 +24,15 @@ public final class CustomKeyStoreSpi extends KeyStoreSpi {
|
||||
|
||||
@Override
|
||||
public Certificate[] engineGetCertificateChain(String alias) {
|
||||
Certificate[] certificates = keyStoreSpi.engineGetCertificateChain(alias);
|
||||
|
||||
// If certificate array is null, throw exception
|
||||
// This shouldn't happen...
|
||||
if (certificates == null) {
|
||||
EntryPoint.LOG("Certificate chain is null!");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// If leaf certificate has attestation extensions, throw exception!
|
||||
if (certificates[0] instanceof X509Certificate x509Certificate) {
|
||||
if (x509Certificate.getExtensionValue(EAT_OID) != null || x509Certificate.getExtensionValue(ASN1_OID) != null || x509Certificate.getExtensionValue(KNOX_OID) != null) {
|
||||
EntryPoint.LOG("Leaf certificate with attestation extensions. Throw exception!");
|
||||
for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {
|
||||
if (stackTraceElement.getClassName().toLowerCase(Locale.US).contains("droidguard")) {
|
||||
EntryPoint.LOG("engineGetCertificateChain invoked by DroidGuard!");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
return certificates;
|
||||
return keyStoreSpi.engineGetCertificateChain(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,7 +14,7 @@ public final class CustomProvider extends Provider {
|
||||
public synchronized Service getService(String type, String algorithm) {
|
||||
EntryPoint.LOG(String.format("Service: '%s' | Algorithm: '%s'", type, algorithm));
|
||||
|
||||
new Thread(EntryPoint::spoofFields).start();
|
||||
EntryPoint.spoofFields();
|
||||
|
||||
return super.getService(type, algorithm);
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
buildscript {
|
||||
val agp_version by extra("8.2.2")
|
||||
}
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("com.android.application") version "8.3.0" apply false
|
||||
}
|
12
changelog.md
12
changelog.md
@ -1,10 +1,14 @@
|
||||
We have a Telegram group!
|
||||
If you want to share your knowledge join:
|
||||
Telegram channel:
|
||||
https://t.me/playintegrityfix
|
||||
|
||||
Device verdict should pass by default.
|
||||
If not, try removing /data/adb/pif.json file.
|
||||
|
||||
# v15.9.4
|
||||
Donations:
|
||||
https://www.paypal.com/paypalme/chiteroman
|
||||
|
||||
- Misc improvements.
|
||||
# v15.9.5
|
||||
|
||||
- Strip libraries and reduce their size.
|
||||
- Fix attestation not passing on some devices.
|
||||
- Do not auto remove conflict apps, users should remove them manually.
|
@ -22,9 +22,7 @@ if [ -f "/data/adb/pif.json" ]; then
|
||||
ui_print "- If pif.json file doesn't exist, module will use default one"
|
||||
fi
|
||||
|
||||
ui_print "- Removing conflict apps..."
|
||||
|
||||
# Remove conflict apps
|
||||
# Conflict apps
|
||||
APPS="
|
||||
/system/app/EliteDevelopmentModule
|
||||
/system/app/XInjectModule
|
||||
@ -35,16 +33,7 @@ APPS="
|
||||
"
|
||||
|
||||
for app in $APPS; do
|
||||
if [ -d "$app" ]; then
|
||||
directory="$MODPATH$app"
|
||||
[ -d "$directory" ] || mkdir -p "$directory"
|
||||
if [ "$KSU" = "true" ] || [ "$APATCH" = "true" ]; then
|
||||
mknod $directory c 0 0
|
||||
else
|
||||
touch $directory/.replace
|
||||
fi
|
||||
ui_print "- ${app##*/} app removed"
|
||||
else
|
||||
ui_print "- ${app##*/} app doesn't exist, skip"
|
||||
fi
|
||||
if [ -d "$app" ]; then
|
||||
ui_print "- ${app##*/} app found! You should uninstall it manually!"
|
||||
fi
|
||||
done
|
||||
|
@ -1,7 +1,7 @@
|
||||
id=playintegrityfix
|
||||
name=Play Integrity Fix
|
||||
version=v15.9.4
|
||||
versionCode=15940
|
||||
version=v15.9.5
|
||||
versionCode=15950
|
||||
author=chiteroman
|
||||
description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8-14.
|
||||
updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json
|
||||
|
@ -36,25 +36,21 @@ fi
|
||||
|
||||
# Late props which must be set after boot_completed
|
||||
{
|
||||
until [ "$(getprop sys.boot_completed)" = "1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
until [[ "$(getprop sys.boot_completed)" == "1" ]]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
resetprop_if_diff ro.boot.flash.locked 1
|
||||
# SafetyNet/Play Integrity | Avoid breaking Realme fingerprint scanners
|
||||
resetprop ro.boot.flash.locked 1
|
||||
|
||||
resetprop_if_diff ro.boot.vbmeta.device_state locked
|
||||
# SafetyNet/Play Integrity | Avoid breaking Oppo fingerprint scanners
|
||||
resetprop ro.boot.vbmeta.device_state locked
|
||||
|
||||
resetprop_if_diff ro.boot.verifiedbootstate green
|
||||
# SafetyNet/Play Integrity | Avoid breaking OnePlus display modes/fingerprint scanners
|
||||
resetprop vendor.boot.verifiedbootstate green
|
||||
|
||||
resetprop_if_diff ro.boot.veritymode enforcing
|
||||
|
||||
resetprop_if_diff vendor.boot.verifiedbootstate green
|
||||
|
||||
resetprop_if_diff vendor.boot.vbmeta.device_state locked
|
||||
|
||||
resetprop_if_diff ro.crypto.state encrypted
|
||||
|
||||
resetprop_if_diff ro.secureboot.lockstate locked
|
||||
|
||||
resetprop_if_diff ro.boot.realmebootstate green
|
||||
# SafetyNet/Play Integrity | Avoid breaking OnePlus display modes/fingerprint scanners on OOS 12
|
||||
resetprop ro.boot.verifiedbootstate green
|
||||
resetprop ro.boot.veritymode enforcing
|
||||
resetprop vendor.boot.vbmeta.device_state locked
|
||||
}&
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "v15.9.4",
|
||||
"versionCode": 15940,
|
||||
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.9.4/PlayIntegrityFix_v15.9.4.zip",
|
||||
"version": "v15.9.5",
|
||||
"versionCode": 15950,
|
||||
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v15.9.5/PlayIntegrityFix_v15.9.5.zip",
|
||||
"changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user