This commit is contained in:
chiteroman 2024-03-18 00:34:07 +01:00
parent 8e239aade9
commit 972bf318cb
13 changed files with 166 additions and 153 deletions

View File

@ -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">

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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);
}

View File

@ -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
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
}&

View File

@ -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"
}