Harcode classes.dex into shared libs, strip their size and more improvements

This commit is contained in:
chiteroman 2023-11-10 17:17:42 +01:00
parent c755de9191
commit 2fbdb4dd61
6 changed files with 1417 additions and 134 deletions

View File

@ -1,19 +1,23 @@
# Play Integrity Fix # Play Integrity Fix
A Zygisk module which fix "ctsProfileMatch" of SafetyNet and "MEETS_DEVICE_INTEGRITY" of 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 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).
Donations: https://paypal.me/chiteroman ## Donations
- [PayPal](https://paypal.me/chiteroman)
XDA Thread: https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/ ## Official posts
- [XDA](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/)
## SafetyNet is depracated and will be replaced by Play Integrity: ## About module
The module will modify few system properties, you can check which prop it modify in these files: "service.sh" and "system.prop". Also you can modify them if you need more. 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 purpouse of the module is to avoid a hardware attestation.
## 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).
@ -21,11 +25,19 @@ If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Int
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.
## 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** **No**
## SafetyNet is depracated
You can read more info here: [click me](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/)
## Current Issues ## Current Issues
* SEEMS TO BE NOT WORKING IN XIAOMI.EU ROMS, WAIT FOR THEIR DEVELOPERS TO FIX IT It doesn't work in Xiaomi.eu custom ROMs due their fix implementation.
Their devs are already working in it: [click me](https://xiaomi.eu/community/threads/google-wallet-stopped-working-device-doesnt-meet-security-requirements.70444/post-704331).
If Xiaomi.eu devs drop support for your device and this module doesn't work you must change the ROM if you want to pass DEVICE verdict.

View File

@ -22,20 +22,13 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments += "-DANDROID_STL=none" arguments += "-DANDROID_STL=none"
arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" arguments += "-DCMAKE_BUILD_TYPE=Release"
cFlags += "-fvisibility=hidden"
cFlags += "-fvisibility-inlines-hidden"
cFlags += "-ffunction-sections"
cFlags += "-fdata-sections"
cppFlags += "-std=c++20" cppFlags += "-std=c++20"
cppFlags += "-fno-exceptions" cppFlags += "-fno-exceptions"
cppFlags += "-fno-rtti" cppFlags += "-fno-rtti"
cppFlags += "-fvisibility=hidden" cppFlags += "-fvisibility=hidden"
cppFlags += "-fvisibility-inlines-hidden" cppFlags += "-fvisibility-inlines-hidden"
cppFlags += "-ffunction-sections"
cppFlags += "-fdata-sections"
} }
} }
} }
@ -62,3 +55,7 @@ android {
ndkVersion = "26.1.10909125" ndkVersion = "26.1.10909125"
buildToolsVersion = "34.0.0" buildToolsVersion = "34.0.0"
} }
dependencies {
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
}

View File

@ -1,3 +1,4 @@
-keep class es.chiteroman.playintegrityfix.EntryPoint {init();} -keep class es.chiteroman.playintegrityfix.EntryPoint {init();}
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi -keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi
-keep class es.chiteroman.playintegrityfix.CustomProvider -keep class es.chiteroman.playintegrityfix.CustomProvider
-keep class org.lsposed.hiddenapibypass.** {*;}

File diff suppressed because it is too large Load Diff

View File

@ -1,58 +1,45 @@
#include <unistd.h> #include <stdlib.h>
#include <string.h>
#include <android/log.h> #include <android/log.h>
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <vector>
#include <string>
#include <map>
#include <fstream>
#include "zygisk.hpp" #include "zygisk.hpp"
#include "dobby.h" #include "dobby.h"
#include "classes_hex.h"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__)
inline static const std::map<std::string, std::string> PROPS_MAP = { static void (*o_callback)(void *, const char *, const char *, uint32_t);
{"ro.product.first_api_level", "24"},
{"ro.secure", "1"},
{"ro.debuggable", "0"},
{"sys.usb.state", "none"},
{"ro.boot.verifiedbootstate", "green"},
{"ro.boot.flash.locked", "1"},
{"ro.boot.vbmeta.device_state", "locked"}
};
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
static std::map<void *, T_Callback> map;
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
if (name != nullptr) { if (o_callback == nullptr) return;
std::string prop(name);
if (PROPS_MAP.contains(prop)) value = PROPS_MAP.at(prop).c_str(); if (cookie != nullptr && name != nullptr && value != nullptr) {
if (!prop.starts_with("cache")) LOGD("[%s] -> %s", name, value); if (strcmp(name, "ro.product.first_api_level") == 0) value = "25";
else if (strcmp(name, "ro.build.version.security_patch") == 0) value = "2018-01-05";
prop.clear(); if (strncmp(name, "cache", 5) != 0) LOGD("[%s] -> %s", name, value);
prop.shrink_to_fit();
} }
return map[cookie](cookie, name, value, serial); return o_callback(cookie, name, value, serial);
} }
static void (*o_system_property_read_callback)(const prop_info *, static void (*o_system_property_read_callback)(const prop_info *,
T_Callback, void (*)(void *, const char *, const char *,
uint32_t),
void *); void *);
static void my_system_property_read_callback(const prop_info *pi, static void my_system_property_read_callback(const prop_info *pi,
T_Callback callback, void (*callback)(void *, const char *, const char *,
uint32_t),
void *cookie) { void *cookie) {
if (pi == nullptr || callback == nullptr || cookie == nullptr) { if (pi == nullptr || callback == nullptr || cookie == nullptr) {
return o_system_property_read_callback(pi, callback, cookie); return o_system_property_read_callback(pi, callback, cookie);
} }
map[cookie] = callback; o_callback = callback;
return o_system_property_read_callback(pi, modify_callback, cookie); return o_system_property_read_callback(pi, modify_callback, cookie);
} }
@ -63,10 +50,10 @@ static void doHook() {
if (handle == nullptr) { if (handle == nullptr) {
LOGD("Couldn't get __system_property_read_callback handle."); LOGD("Couldn't get __system_property_read_callback handle.");
return; return;
} else {
LOGD("Got __system_property_read_callback handle and hooked it at %p", handle);
} }
LOGD("Got __system_property_read_callback handle and hooked it at %p", handle);
DobbyHook(handle, (void *) my_system_property_read_callback, DobbyHook(handle, (void *) my_system_property_read_callback,
(void **) &o_system_property_read_callback); (void **) &o_system_property_read_callback);
} }
@ -79,42 +66,16 @@ public:
} }
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr); bool isGms = false;
std::string process(rawProcess);
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
if (process.starts_with("com.google.android.gms")) { auto process = env->GetStringUTFChars(args->nice_name, nullptr);
if (process != nullptr) {
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); isGms = strncmp(process, "com.google.android.gms", 22) == 0;
isGmsUnstable = strcmp(process, "com.google.android.gms.unstable") == 0;
if (process == "com.google.android.gms.unstable") {
isGmsUnstable = true;
int fd = api->connectCompanion();
long size;
read(fd, &size, sizeof(size));
if (size > 0) {
LOGD("Received %ld bytes from socket", size);
char buffer[size];
read(fd, buffer, size);
buffer[size] = 0;
moduleDex.insert(moduleDex.end(), buffer, buffer + size);
} else {
LOGD("Received invalid bytes from socket. Does classes.dex file exist?");
}
close(fd);
}
} }
env->ReleaseStringUTFChars(args->nice_name, process);
process.clear(); if (isGms) api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
process.shrink_to_fit();
if (!isGmsUnstable) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); if (!isGmsUnstable) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
} }
@ -123,8 +84,7 @@ public:
if (!isGmsUnstable) return; if (!isGmsUnstable) return;
doHook(); doHook();
injectDex();
if (!moduleDex.empty()) injectDex();
} }
void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override {
@ -135,7 +95,6 @@ private:
zygisk::Api *api = nullptr; zygisk::Api *api = nullptr;
JNIEnv *env = nullptr; JNIEnv *env = nullptr;
bool isGmsUnstable = false; bool isGmsUnstable = false;
std::vector<char> moduleDex;
void injectDex() { void injectDex() {
LOGD("get system classloader"); LOGD("get system classloader");
@ -145,7 +104,7 @@ private:
auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader); auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader);
LOGD("create buffer"); LOGD("create buffer");
auto buf = env->NewDirectByteBuffer(moduleDex.data(), static_cast<jlong>(moduleDex.size())); auto buf = env->NewDirectByteBuffer(classes_dex, classes_dex_len);
LOGD("create class loader"); LOGD("create class loader");
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
auto dexClInit = env->GetMethodID(dexClClass, "<init>", auto dexClInit = env->GetMethodID(dexClClass, "<init>",
@ -164,8 +123,6 @@ private:
env->CallStaticVoidMethod(entryClass, entryInit); env->CallStaticVoidMethod(entryClass, entryInit);
LOGD("clean"); LOGD("clean");
moduleDex.clear();
moduleDex.shrink_to_fit();
env->DeleteLocalRef(clClass); env->DeleteLocalRef(clClass);
env->DeleteLocalRef(systemClassLoader); env->DeleteLocalRef(systemClassLoader);
env->DeleteLocalRef(buf); env->DeleteLocalRef(buf);
@ -177,32 +134,4 @@ private:
} }
}; };
static void companion(int fd) {
std::ifstream ifs("/data/adb/modules/playintegrityfix/classes.dex",
std::ios::binary | std::ios::ate);
if (ifs.bad()) {
long i = -1;
write(fd, &i, sizeof(i));
close(fd);
return;
}
long size = ifs.tellg();
ifs.seekg(std::ios::beg);
char buffer[size];
ifs.read(buffer, size);
buffer[size] = 0;
ifs.close();
write(fd, &size, sizeof(size));
write(fd, buffer, size);
close(fd);
}
REGISTER_ZYGISK_MODULE(PlayIntegrityFix) REGISTER_ZYGISK_MODULE(PlayIntegrityFix)
REGISTER_ZYGISK_COMPANION(companion)

View File

@ -3,23 +3,41 @@ package es.chiteroman.playintegrityfix;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import org.lsposed.hiddenapibypass.HiddenApiBypass;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.KeyStoreSpi; import java.security.KeyStoreSpi;
import java.security.Provider; import java.security.Provider;
import java.security.Security; import java.security.Security;
import java.util.List;
public class EntryPoint { public class EntryPoint {
private static List<Field> fields;
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
fields = HiddenApiBypass.getStaticFields(Class.forName("android.os.Build"));
LOG("Fields added in list: " + fields.size());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
public static void init() { public static void init() {
spoofDevice(); spoofDevice();
spoofProvider(); spoofProvider();
} }
private static void spoofProvider() { private static void spoofProvider() {
final String KEYSTORE = "AndroidKeyStore";
try { try {
Provider provider = Security.getProvider("AndroidKeyStore"); Provider provider = Security.getProvider(KEYSTORE);
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); KeyStore keyStore = KeyStore.getInstance(KEYSTORE);
Field f = keyStore.getClass().getDeclaredField("keyStoreSpi"); Field f = keyStore.getClass().getDeclaredField("keyStoreSpi");
f.setAccessible(true); f.setAccessible(true);
@ -27,7 +45,7 @@ public class EntryPoint {
f.setAccessible(false); f.setAccessible(false);
CustomProvider customProvider = new CustomProvider(provider); CustomProvider customProvider = new CustomProvider(provider);
Security.removeProvider("AndroidKeyStore"); Security.removeProvider(KEYSTORE);
Security.insertProviderAt(customProvider, 1); Security.insertProviderAt(customProvider, 1);
LOG("Spoof KeyStoreSpi and Provider done!"); LOG("Spoof KeyStoreSpi and Provider done!");
@ -42,30 +60,43 @@ public class EntryPoint {
} }
public static void spoofDevice() { public static void spoofDevice() {
setProp("PRODUCT", "WW_Phone"); final String PRODUCT = "WW_Phone";
setProp("PRODUCT_FOR_ATTESTATION", "WW_Phone"); final String DEVICE = "ASUS_X00HD_4";
final String MANUFACTURER = "Asus";
final String BRAND = "Asus";
final String MODEL = "ASUS_X00HD";
final String FINGERPRINT = "asus/WW_Phone/ASUS_X00HD_4:7.1.1/NMF26F/14.2016.1801.372-20180119:user/release-keys";
setProp("DEVICE", "ASUS_X00HD_4"); setProp("PRODUCT", PRODUCT);
setProp("DEVICE_FOR_ATTESTATION", "ASUS_X00HD_4"); setProp("DEVICE", DEVICE);
setProp("MANUFACTURER", MANUFACTURER);
setProp("BRAND", BRAND);
setProp("MODEL", MODEL);
setProp("MANUFACTURER", "Asus"); setProp("PRODUCT_FOR_ATTESTATION", PRODUCT);
setProp("MANUFACTURER_FOR_ATTESTATION", "Asus"); setProp("DEVICE_FOR_ATTESTATION", DEVICE);
setProp("MANUFACTURER_FOR_ATTESTATION", MANUFACTURER);
setProp("BRAND_FOR_ATTESTATION", BRAND);
setProp("MODEL_FOR_ATTESTATION", MODEL);
setProp("BRAND", "Asus"); setProp("FINGERPRINT", FINGERPRINT);
setProp("BRAND_FOR_ATTESTATION", "Asus");
setProp("MODEL", "ASUS_X00HD");
setProp("MODEL_FOR_ATTESTATION", "ASUS_X00HD");
setProp("FINGERPRINT", "asus/WW_Phone/ASUS_X00HD_4:7.1.1/NMF26F/14.2016.1801.372-20180119:user/release-keys");
} }
private static void setProp(String name, String value) { private static void setProp(String name, String value) {
try { try {
Field f = Build.class.getDeclaredField(name); Field field = null;
f.setAccessible(true); if (fields != null && !fields.isEmpty()) {
f.set(null, value); for (Field f : fields) {
f.setAccessible(false); if (f.getName().equals(name)) {
field = f;
break;
}
}
}
if (field == null) field = Build.class.getDeclaredField(name);
field.setAccessible(true);
field.set(null, value);
field.setAccessible(false);
LOG(String.format("Modified field '%s' with value '%s'", name, value)); LOG(String.format("Modified field '%s' with value '%s'", name, value));
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
LOG(String.format("Couldn't find '%s' field name.", name)); LOG(String.format("Couldn't find '%s' field name.", name));