From 972bf318cb292d1c76fce77d7ec2c9f5fa65acdf Mon Sep 17 00:00:00 2001 From: chiteroman <98092901+chiteroman@users.noreply.github.com> Date: Mon, 18 Mar 2024 00:34:07 +0100 Subject: [PATCH] v15.9.5 --- .idea/misc.xml | 1 + README.md | 47 +++++- app/build.gradle.kts | 19 ++- app/src/main/cpp/CMakeLists.txt | 4 + app/src/main/cpp/main.cpp | 138 +++++++++--------- .../playintegrityfix/CustomKeyStoreSpi.java | 24 +-- .../playintegrityfix/CustomProvider.java | 2 +- build.gradle.kts | 5 +- changelog.md | 12 +- module/customize.sh | 19 +-- module/module.prop | 4 +- module/service.sh | 38 +++-- update.json | 6 +- 13 files changed, 166 insertions(+), 153 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..0ad17cb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + diff --git a/README.md b/README.md index 489f544..dd06f8e 100644 --- a/README.md +++ b/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 \ No newline at end of file +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) \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0cf6df9..c6b7439 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 714447d..6c9464f 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -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) diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index d5afe9e..8be494b 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -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 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 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 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 { diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java index a484c96..8820bd4 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java @@ -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 diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java index a185272..0d958fd 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -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); } diff --git a/build.gradle.kts b/build.gradle.kts index c509c00..bad4dce 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 } \ No newline at end of file diff --git a/changelog.md b/changelog.md index d6b472b..f00ba01 100644 --- a/changelog.md +++ b/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. \ No newline at end of file +# 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. \ No newline at end of file diff --git a/module/customize.sh b/module/customize.sh index f6eaa29..a70e81d 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -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 diff --git a/module/module.prop b/module/module.prop index a849d54..1820a00 100644 --- a/module/module.prop +++ b/module/module.prop @@ -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 diff --git a/module/service.sh b/module/service.sh index b9792f0..621183a 100644 --- a/module/service.sh +++ b/module/service.sh @@ -36,25 +36,21 @@ fi # Late props which must be set after boot_completed { - until [ "$(getprop sys.boot_completed)" = "1" ]; do - sleep 1 - done - - resetprop_if_diff ro.boot.flash.locked 1 - - resetprop_if_diff ro.boot.vbmeta.device_state locked - - resetprop_if_diff ro.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 + until [[ "$(getprop sys.boot_completed)" == "1" ]]; do + sleep 1 + done + + # SafetyNet/Play Integrity | Avoid breaking Realme fingerprint scanners + resetprop ro.boot.flash.locked 1 + + # SafetyNet/Play Integrity | Avoid breaking Oppo fingerprint scanners + resetprop ro.boot.vbmeta.device_state locked + + # SafetyNet/Play Integrity | Avoid breaking OnePlus display modes/fingerprint scanners + resetprop vendor.boot.verifiedbootstate 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 }& diff --git a/update.json b/update.json index 301d585..d02f0fd 100644 --- a/update.json +++ b/update.json @@ -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" }