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
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:
- 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).
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:
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.
## About module
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:
- Disable all modules except mine.
- 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.
## Read module logs
You can read module logs using this command:
```
adb shell "logcat | grep 'PIF'"
```
## Can this module pass MEETS_STRONG_INTEGRITY?
**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
* 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 {
cmake {
arguments += "-DANDROID_STL=none"
arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
cFlags += "-fvisibility=hidden"
cFlags += "-fvisibility-inlines-hidden"
cFlags += "-ffunction-sections"
cFlags += "-fdata-sections"
arguments += "-DCMAKE_BUILD_TYPE=Release"
cppFlags += "-std=c++20"
cppFlags += "-fno-exceptions"
cppFlags += "-fno-rtti"
cppFlags += "-fvisibility=hidden"
cppFlags += "-fvisibility-inlines-hidden"
cppFlags += "-ffunction-sections"
cppFlags += "-fdata-sections"
}
}
}
@ -62,3 +55,7 @@ android {
ndkVersion = "26.1.10909125"
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.CustomKeyStoreSpi
-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 <sys/system_properties.h>
#include <vector>
#include <string>
#include <map>
#include <fstream>
#include "zygisk.hpp"
#include "dobby.h"
#include "classes_hex.h"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__)
inline static const std::map<std::string, std::string> PROPS_MAP = {
{"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 (*o_callback)(void *, const char *, const char *, uint32_t);
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
if (name != nullptr) {
std::string prop(name);
if (o_callback == nullptr) return;
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();
prop.shrink_to_fit();
if (strncmp(name, "cache", 5) != 0) LOGD("[%s] -> %s", name, value);
}
return map[cookie](cookie, name, value, serial);
return o_callback(cookie, name, value, serial);
}
static void (*o_system_property_read_callback)(const prop_info *,
T_Callback,
void (*)(void *, const char *, const char *,
uint32_t),
void *);
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) {
if (pi == nullptr || callback == nullptr || cookie == nullptr) {
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);
}
@ -63,10 +50,10 @@ static void doHook() {
if (handle == nullptr) {
LOGD("Couldn't get __system_property_read_callback handle.");
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,
(void **) &o_system_property_read_callback);
}
@ -79,42 +66,16 @@ public:
}
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
std::string process(rawProcess);
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
bool isGms = false;
if (process.starts_with("com.google.android.gms")) {
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
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);
}
auto process = env->GetStringUTFChars(args->nice_name, nullptr);
if (process != nullptr) {
isGms = strncmp(process, "com.google.android.gms", 22) == 0;
isGmsUnstable = strcmp(process, "com.google.android.gms.unstable") == 0;
}
env->ReleaseStringUTFChars(args->nice_name, process);
process.clear();
process.shrink_to_fit();
if (isGms) api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
if (!isGmsUnstable) api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
}
@ -123,8 +84,7 @@ public:
if (!isGmsUnstable) return;
doHook();
if (!moduleDex.empty()) injectDex();
injectDex();
}
void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override {
@ -135,7 +95,6 @@ private:
zygisk::Api *api = nullptr;
JNIEnv *env = nullptr;
bool isGmsUnstable = false;
std::vector<char> moduleDex;
void injectDex() {
LOGD("get system classloader");
@ -145,7 +104,7 @@ private:
auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader);
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");
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
auto dexClInit = env->GetMethodID(dexClClass, "<init>",
@ -164,8 +123,6 @@ private:
env->CallStaticVoidMethod(entryClass, entryInit);
LOGD("clean");
moduleDex.clear();
moduleDex.shrink_to_fit();
env->DeleteLocalRef(clClass);
env->DeleteLocalRef(systemClassLoader);
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_COMPANION(companion)

View File

@ -3,23 +3,41 @@ package es.chiteroman.playintegrityfix;
import android.os.Build;
import android.util.Log;
import org.lsposed.hiddenapibypass.HiddenApiBypass;
import java.lang.reflect.Field;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.Provider;
import java.security.Security;
import java.util.List;
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() {
spoofDevice();
spoofProvider();
}
private static void spoofProvider() {
final String KEYSTORE = "AndroidKeyStore";
try {
Provider provider = Security.getProvider("AndroidKeyStore");
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
Provider provider = Security.getProvider(KEYSTORE);
KeyStore keyStore = KeyStore.getInstance(KEYSTORE);
Field f = keyStore.getClass().getDeclaredField("keyStoreSpi");
f.setAccessible(true);
@ -27,7 +45,7 @@ public class EntryPoint {
f.setAccessible(false);
CustomProvider customProvider = new CustomProvider(provider);
Security.removeProvider("AndroidKeyStore");
Security.removeProvider(KEYSTORE);
Security.insertProviderAt(customProvider, 1);
LOG("Spoof KeyStoreSpi and Provider done!");
@ -42,30 +60,43 @@ public class EntryPoint {
}
public static void spoofDevice() {
setProp("PRODUCT", "WW_Phone");
setProp("PRODUCT_FOR_ATTESTATION", "WW_Phone");
final String PRODUCT = "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("DEVICE_FOR_ATTESTATION", "ASUS_X00HD_4");
setProp("PRODUCT", PRODUCT);
setProp("DEVICE", DEVICE);
setProp("MANUFACTURER", MANUFACTURER);
setProp("BRAND", BRAND);
setProp("MODEL", MODEL);
setProp("MANUFACTURER", "Asus");
setProp("MANUFACTURER_FOR_ATTESTATION", "Asus");
setProp("PRODUCT_FOR_ATTESTATION", PRODUCT);
setProp("DEVICE_FOR_ATTESTATION", DEVICE);
setProp("MANUFACTURER_FOR_ATTESTATION", MANUFACTURER);
setProp("BRAND_FOR_ATTESTATION", BRAND);
setProp("MODEL_FOR_ATTESTATION", MODEL);
setProp("BRAND", "Asus");
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");
setProp("FINGERPRINT", FINGERPRINT);
}
private static void setProp(String name, String value) {
try {
Field f = Build.class.getDeclaredField(name);
f.setAccessible(true);
f.set(null, value);
f.setAccessible(false);
Field field = null;
if (fields != null && !fields.isEmpty()) {
for (Field f : fields) {
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));
} catch (NoSuchFieldException e) {
LOG(String.format("Couldn't find '%s' field name.", name));