diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 52a8b6ff..f47207a5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "es.chiteroman.playintegrityfix" minSdk = 26 targetSdk = 34 - versionCode = 16000 - versionName = "v16.0" + versionCode = 16300 + versionName = "v16.3" multiDexEnabled = false packaging { @@ -94,7 +94,7 @@ tasks.register("copyFiles") { 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/stripReleaseDebugSymbols/out/lib") -// dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true) + dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true) soDir.walk().filter { it.isFile && it.extension == "so" }.forEach { soFile -> val abiFolder = soFile.parentFile.name diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c65bd566..31d16a33 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,3 +1,4 @@ -keep class es.chiteroman.playintegrityfix.EntryPoint {public ;} -keep class es.chiteroman.playintegrityfix.CustomProvider --keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi \ No newline at end of file +-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi +-keep class es.chiteroman.playintegrityfix.KeyboxUtils \ No newline at end of file diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index d8062c23..8fa08892 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -1,20 +1,24 @@ #include #include #include +#include #include "dobby.h" #include "json.hpp" #include "zygisk.hpp" -#include "dex.h" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__) +#define DEX_PATH "/data/adb/modules/playintegrityfix/classes.dex" + #define PIF_JSON "/data/adb/pif.json" #define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json" -static std::string DEVICE_INITIAL_SDK_INT, SECURITY_PATCH, ID; +#define KEYBOX_JSON "/data/adb/keybox.xml" + +#define KEYBOX_JSON_DEFAULT "/data/adb/modules/playintegrityfix/keybox.xml" -static inline ssize_t xread(int fd, void *buffer, size_t count) { +static ssize_t xread(int fd, void *buffer, size_t count) { ssize_t total = 0; char *buf = (char *) buffer; while (count > 0) { @@ -27,7 +31,7 @@ static inline ssize_t xread(int fd, void *buffer, size_t count) { return total; } -static inline ssize_t xwrite(int fd, void *buffer, size_t count) { +static ssize_t xwrite(int fd, void *buffer, size_t count) { ssize_t total = 0; char *buf = (char *) buffer; while (count > 0) { @@ -40,6 +44,8 @@ static inline ssize_t xwrite(int fd, void *buffer, size_t count) { return total; } +static std::string DEVICE_INITIAL_SDK_INT; + typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); static T_Callback o_callback = nullptr; @@ -50,17 +56,8 @@ static void modify_callback(void *cookie, const char *name, const char *value, u std::string_view prop(name); - if (prop.ends_with("api_level") && !DEVICE_INITIAL_SDK_INT.empty()) { + if (prop.ends_with("first_api_level") && !DEVICE_INITIAL_SDK_INT.empty()) { value = DEVICE_INITIAL_SDK_INT.c_str(); - } else if (prop.ends_with(".security_patch") && !SECURITY_PATCH.empty()) { - value = SECURITY_PATCH.c_str(); - } else if (prop.ends_with(".id") && !ID.empty()) { - value = ID.c_str(); - } else if (prop == "sys.usb.state") { - value = "none"; - } - - if (!prop.starts_with("persist") && !prop.starts_with("cache") && !prop.starts_with("debug")) { LOGD("[%s]: %s", name, value); } @@ -98,71 +95,81 @@ class PlayIntegrityFix : public zygisk::ModuleBase { void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - if (!args) { api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); return; } - const char *dir = env->GetStringUTFChars(args->app_data_dir, nullptr); + auto dir = env->GetStringUTFChars(args->app_data_dir, nullptr); if (!dir) { api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); return; } - if (!std::string_view(dir).ends_with("/com.google.android.gms")) { - env->ReleaseStringUTFChars(args->app_data_dir, dir); + bool 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; } - env->ReleaseStringUTFChars(args->app_data_dir, dir); - api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - const char *name = env->GetStringUTFChars(args->nice_name, nullptr); + auto name = env->GetStringUTFChars(args->nice_name, nullptr); if (!name) { api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); return; } - if (strncmp(name, "com.google.android.gms.unstable", 31) != 0) { - env->ReleaseStringUTFChars(args->nice_name, name); + 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; } - env->ReleaseStringUTFChars(args->nice_name, name); + int fd = api->connectCompanion(); - long size = 0; - std::vector vector; + int dexSize = 0, jsonSize = 0, keyboxSize = 0; + std::vector jsonVector, keyboxVector; - int fd = api->connectCompanion(); + xread(fd, &dexSize, sizeof(int)); + xread(fd, &jsonSize, sizeof(int)); + xread(fd, &keyboxSize, sizeof(int)); - xread(fd, &size, sizeof(long)); + dexVector.resize(dexSize); + xread(fd, dexVector.data(), dexSize); - if (size > 0) { - vector.resize(size); - xread(fd, vector.data(), size); - json = nlohmann::json::parse(vector, nullptr, false, true); - } else { - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - } + jsonVector.resize(jsonSize); + xread(fd, jsonVector.data(), jsonSize); + + keyboxVector.resize(keyboxSize); + xread(fd, keyboxVector.data(), keyboxSize); close(fd); + + json = nlohmann::json::parse(jsonVector, nullptr, false, true); + + keyboxString = std::string(keyboxVector.cbegin(), keyboxVector.cend()); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (json.empty()) return; + if (dexVector.empty() || json.empty()) return; + + if (json.contains("DEVICE_INITIAL_SDK_INT")) { + DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get(); + json.erase("DEVICE_INITIAL_SDK_INT"); // You can't modify field value + } - parseJson(); + doHook(); injectDex(); - -// doHook(); } void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { @@ -172,20 +179,9 @@ class PlayIntegrityFix : public zygisk::ModuleBase { private: zygisk::Api *api = nullptr; JNIEnv *env = nullptr; + std::vector dexVector; nlohmann::json json; - - void parseJson() { - if (json.contains("DEVICE_INITIAL_SDK_INT")) { - DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get(); - json.erase("DEVICE_INITIAL_SDK_INT"); // You can't modify field value - } - if (json.contains("SECURITY_PATCH")) { - SECURITY_PATCH = json["SECURITY_PATCH"].get(); - } - if (json.contains("ID")) { - ID = json["ID"].get(); - } - } + std::string keyboxString; void injectDex() { LOGD("get system classloader"); @@ -198,7 +194,7 @@ class PlayIntegrityFix : public zygisk::ModuleBase { auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClInit = env->GetMethodID(dexClClass, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - auto buffer = env->NewDirectByteBuffer(classes_dex, classes_dex_len); + auto buffer = env->NewDirectByteBuffer(dexVector.data(), dexVector.size()); auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); LOGD("load class"); @@ -210,45 +206,47 @@ class PlayIntegrityFix : public zygisk::ModuleBase { auto entryPointClass = (jclass) entryClassObj; LOGD("call init"); - auto entryInit = env->GetStaticMethodID(entryPointClass, "init", "(Ljava/lang/String;)V"); - auto str = env->NewStringUTF(json.dump().c_str()); - env->CallStaticVoidMethod(entryPointClass, entryInit, str); + auto entryInit = env->GetStaticMethodID(entryPointClass, "init", + "(Ljava/lang/String;Ljava/lang/String;)V"); + auto jsonStr = env->NewStringUTF(json.dump().c_str()); + auto keyboxStr = env->NewStringUTF(keyboxString.c_str()); + env->CallStaticVoidMethod(entryPointClass, entryInit, jsonStr, keyboxStr); } }; -static std::vector readFile(const char *path) { - - std::vector vector; - - FILE *file = fopen(path, "rb"); +static std::vector readFile(const std::string &path) { - if (file) { - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); + std::ifstream ifs(path); - vector.resize(size); - fread(vector.data(), 1, size, file); - fclose(file); - } else { - LOGD("Couldn't read %s file!", path); + if (!ifs || ifs.bad()) { + return std::vector(); } - return vector; + return std::vector((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); } static void companion(int fd) { - long size = 0; - std::vector vector; - vector = readFile(PIF_JSON); + auto dex = readFile(DEX_PATH); + + auto json = readFile(PIF_JSON); + if (json.empty()) json = readFile(PIF_JSON_DEFAULT); + + auto keybox = readFile(KEYBOX_JSON); + if (keybox.empty()) keybox = readFile(KEYBOX_JSON_DEFAULT); - if (vector.empty()) vector = readFile(PIF_JSON_DEFAULT); + int dexSize = dex.size(); + int jsonSize = json.size(); + int keyboxSize = keybox.size(); - size = vector.size(); + xwrite(fd, &dexSize, sizeof(int)); + xwrite(fd, &jsonSize, sizeof(int)); + xwrite(fd, &keyboxSize, sizeof(int)); - xwrite(fd, &size, sizeof(long)); - xwrite(fd, vector.data(), size); + xwrite(fd, dex.data(), dexSize * sizeof(char)); + xwrite(fd, json.data(), jsonSize * sizeof(char)); + xwrite(fd, keybox.data(), keyboxSize * sizeof(char)); } REGISTER_ZYGISK_MODULE(PlayIntegrityFix) diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java index 1f4b170c..853b5db2 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomKeyStoreSpi.java @@ -26,7 +26,7 @@ public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmExc @Override public Certificate[] engineGetCertificateChain(String alias) { if (Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.US).contains("droidguard"))) { - return Android.engineGetCertificateChain(keyStoreSpi.engineGetCertificateChain(alias)); + return KeyboxUtils.engineGetCertificateChain(keyStoreSpi.engineGetCertificateChain(alias)); } return keyStoreSpi.engineGetCertificateChain(alias); } diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index 38077609..c4786008 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -37,7 +37,7 @@ public final class EntryPoint { Security.insertProviderAt(customProvider, 1); } - public static void init(String json) { + public static void init(String json, String kbox) { try { JSONObject jsonObject = new JSONObject(json); @@ -65,6 +65,12 @@ public static void init(String json) { } catch (Throwable t) { LOG("Error loading json file: " + t); } + + try { + KeyboxUtils.parseXml(kbox); + } catch (Throwable t) { + LOG("Error parsing keybox file: " + t); + } } static void spoofFields() { diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/Keybox.java b/app/src/main/java/es/chiteroman/playintegrityfix/Keybox.java deleted file mode 100644 index b2c0521a..00000000 --- a/app/src/main/java/es/chiteroman/playintegrityfix/Keybox.java +++ /dev/null @@ -1,107 +0,0 @@ -package es.chiteroman.playintegrityfix; - -public final class Keybox { - public static final class EC { - public static final String PRIVATE_KEY = """ - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49 - AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l - uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA== - -----END EC PRIVATE KEY----- - """; - public static final String CERTIFICATE_1 = """ - -----BEGIN CERTIFICATE----- - MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw - EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD - VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu - ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx - MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE - CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB - bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz - dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue - efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8 - U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R - qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG - AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8 - wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9 - Xvsiu+f+uXc/WT/7 - -----END CERTIFICATE----- - """; - public static final String CERTIFICATE_2 = """ - -----BEGIN CERTIFICATE----- - MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG - EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll - dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD - VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw - HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx - EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT - BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq - QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH - KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59 - dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O - BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W - EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG - SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN - C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw== - -----END CERTIFICATE----- - """; - } - - public static final class RSA { - public static final String PRIVATE_KEY = """ - -----BEGIN RSA PRIVATE KEY----- - MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb - KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI - O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB - AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3 - wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C - woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs - NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU - 0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy - DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY - ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3 - TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y - qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x - fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l - -----END RSA PRIVATE KEY----- - """; - public static final String CERTIFICATE_1 = """ - -----BEGIN CERTIFICATE----- - MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx - EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT - BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx - MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD - YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv - aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf - MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1 - qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX - 4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd - XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ - k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw - BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn - M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4 - Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f - 9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8= - -----END CERTIFICATE----- - """; - public static final String CERTIFICATE_2 = """ - -----BEGIN CERTIFICATE----- - MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV - BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW - aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN - MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG - A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE - CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB - AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs - Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL - t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID - AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw - FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B - Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j - HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc - DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op - Lvtc9mL1J1IXvmM= - -----END CERTIFICATE----- - """; - } -} diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/Android.java b/app/src/main/java/es/chiteroman/playintegrityfix/KeyboxUtils.java similarity index 58% rename from app/src/main/java/es/chiteroman/playintegrityfix/Android.java rename to app/src/main/java/es/chiteroman/playintegrityfix/KeyboxUtils.java index 155ac6bd..3c1c19d8 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/Android.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/KeyboxUtils.java @@ -1,6 +1,7 @@ package es.chiteroman.playintegrityfix; import android.security.keystore.KeyProperties; +import android.text.TextUtils; import org.spongycastle.asn1.ASN1Boolean; import org.spongycastle.asn1.ASN1Encodable; @@ -23,39 +24,84 @@ import org.spongycastle.operator.ContentSigner; import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; import org.spongycastle.util.io.pem.PemReader; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import java.io.ByteArrayInputStream; import java.io.StringReader; import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.LinkedList; -import java.util.List; import java.util.concurrent.ThreadLocalRandom; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; -public final class Android { - private static final PEMKeyPair EC, RSA; +public final class KeyboxUtils { private static final ASN1ObjectIdentifier OID = new ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17"); - private static final List EC_CERTS = new ArrayList<>(); - private static final List RSA_CERTS = new ArrayList<>(); + private static final LinkedList EC_CERTS = new LinkedList<>(); + private static final LinkedList RSA_CERTS = new LinkedList<>(); private static final CertificateFactory certificateFactory; + private static PEMKeyPair EC, RSA; static { try { certificateFactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } - EC = parseKeyPair(Keybox.EC.PRIVATE_KEY); - EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_1)); - EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_2)); - - RSA = parseKeyPair(Keybox.RSA.PRIVATE_KEY); - RSA_CERTS.add(parseCert(Keybox.RSA.CERTIFICATE_1)); - RSA_CERTS.add(parseCert(Keybox.RSA.CERTIFICATE_2)); - } catch (Throwable t) { - EntryPoint.LOG(t.toString()); - throw new RuntimeException(t); + public static void parseXml(String kbox) throws Throwable { + if (TextUtils.isEmpty(kbox)) return; + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(kbox.getBytes())); + + doc.getDocumentElement().normalize(); + + NodeList keyboxList = doc.getElementsByTagName("Keybox"); + Node keyboxNode = keyboxList.item(0); + if (keyboxNode.getNodeType() == Node.ELEMENT_NODE) { + Element keyboxElement = (Element) keyboxNode; + + NodeList keyList = keyboxElement.getElementsByTagName("Key"); + for (int j = 0; j < keyList.getLength(); j++) { + Element keyElement = (Element) keyList.item(j); + String algorithm = keyElement.getAttribute("algorithm"); + + NodeList privateKeyList = keyElement.getElementsByTagName("PrivateKey"); + if (privateKeyList.getLength() > 0) { + Element privateKeyElement = (Element) privateKeyList.item(0); + String privateKeyContent = privateKeyElement.getTextContent().trim(); + if ("ecdsa".equals(algorithm)) { + EC = parseKeyPair(privateKeyContent); + } else if ("rsa".equals(algorithm)) { + RSA = parseKeyPair(privateKeyContent); + } + } + + NodeList certificateChainList = keyElement.getElementsByTagName("CertificateChain"); + if (certificateChainList.getLength() > 0) { + Element certificateChainElement = (Element) certificateChainList.item(0); + + NodeList certificateList = certificateChainElement.getElementsByTagName("Certificate"); + for (int k = 0; k < certificateList.getLength(); k++) { + Element certificateElement = (Element) certificateList.item(k); + String certificateContent = certificateElement.getTextContent().trim(); + if ("ecdsa".equals(algorithm)) { + EC_CERTS.add(parseCert(certificateContent)); + } else if ("rsa".equals(algorithm)) { + RSA_CERTS.add(parseCert(certificateContent)); + } + } + } + } } } @@ -72,7 +118,18 @@ private static Certificate parseCert(String cert) throws Throwable { } public static Certificate[] engineGetCertificateChain(Certificate[] caList) { - if (caList == null) throw new UnsupportedOperationException(); + if (caList == null) { + EntryPoint.LOG("Certificate chain is null!"); + throw new UnsupportedOperationException(); + } + if (EC == null && RSA == null) { + EntryPoint.LOG("EC and RSA private keys are null!"); + throw new UnsupportedOperationException(); + } + if (EC_CERTS.isEmpty() && RSA_CERTS.isEmpty()) { + EntryPoint.LOG("EC and RSA certs are empty!"); + throw new UnsupportedOperationException(); + } try { X509Certificate leaf = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(caList[0].getEncoded())); @@ -103,14 +160,17 @@ public static Certificate[] engineGetCertificateChain(Certificate[] caList) { X509v3CertificateBuilder builder; ContentSigner signer; - if (KeyProperties.KEY_ALGORITHM_EC.equals(leaf.getPublicKey().getAlgorithm())) { + // Not all keyboxes have EC keys :) + if (EC != null && !EC_CERTS.isEmpty() && KeyProperties.KEY_ALGORITHM_EC.equals(leaf.getPublicKey().getAlgorithm())) { + EntryPoint.LOG("Using EC"); certificates = new LinkedList<>(EC_CERTS); builder = new X509v3CertificateBuilder(new X509CertificateHolder(EC_CERTS.get(0).getEncoded()).getSubject(), holder.getSerialNumber(), holder.getNotBefore(), holder.getNotAfter(), holder.getSubject(), EC.getPublicKeyInfo()); signer = new JcaContentSignerBuilder(leaf.getSigAlgName()).build(new JcaPEMKeyConverter().getPrivateKey(EC.getPrivateKeyInfo())); } else { + EntryPoint.LOG("Using RSA"); certificates = new LinkedList<>(RSA_CERTS); builder = new X509v3CertificateBuilder(new X509CertificateHolder(RSA_CERTS.get(0).getEncoded()).getSubject(), holder.getSerialNumber(), holder.getNotBefore(), holder.getNotAfter(), holder.getSubject(), RSA.getPublicKeyInfo()); - signer = new JcaContentSignerBuilder(leaf.getSigAlgName()).build(new JcaPEMKeyConverter().getPrivateKey(RSA.getPrivateKeyInfo())); + signer = new JcaContentSignerBuilder("SHA256withRSA").build(new JcaPEMKeyConverter().getPrivateKey(RSA.getPrivateKeyInfo())); } byte[] verifiedBootKey = new byte[32]; @@ -151,6 +211,6 @@ public static Certificate[] engineGetCertificateChain(Certificate[] caList) { } catch (Throwable t) { EntryPoint.LOG(t.toString()); } - return caList; + throw new UnsupportedOperationException(); } } \ No newline at end of file diff --git a/changelog.md b/changelog.md index 051cef2a..9b739033 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,16 @@ If not, try removing /data/adb/pif.json file. Donations: https://www.paypal.com/paypalme/chiteroman -# v16.0 +# v16.3 -- WORKING TEE = STRONG PASS +Google fixed the bug, no more Strong pass with SW keybox 😢 + +- Improve C++ and Java code +- Downgrade first_api_level to 24, so all devices (should) be able to pass Device +- Included keybox.xml parsing! You can create /data/adb/keybox.xml to define your own keybox (Strong passing with my private one :D) + +By default, inside module folder, it exits pif.json and keybox.xml, do NOT delete these files + +keybox.xml included in the module is SW one + +Keybox "hack" does NOT work on broken TEE devices, like OnePlus diff --git a/module/keybox.xml b/module/keybox.xml new file mode 100644 index 00000000..9c029966 --- /dev/null +++ b/module/keybox.xml @@ -0,0 +1,114 @@ + + + 1 + + + + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49 + AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l + uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA== + -----END EC PRIVATE KEY----- + + + 2 + + -----BEGIN CERTIFICATE----- + MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw + EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD + VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu + ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx + MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE + CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB + bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz + dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue + efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8 + U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R + qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG + AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8 + wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9 + Xvsiu+f+uXc/WT/7 + -----END CERTIFICATE----- + + + -----BEGIN CERTIFICATE----- + MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG + EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll + dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD + VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw + HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx + EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT + BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq + QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH + KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59 + dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O + BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W + EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG + SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN + C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw== + -----END CERTIFICATE----- + + + + + + -----BEGIN RSA PRIVATE KEY----- + MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb + KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI + O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB + AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3 + wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C + woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs + NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU + 0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy + DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY + ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3 + TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y + qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x + fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l + -----END RSA PRIVATE KEY----- + + + 2 + + -----BEGIN CERTIFICATE----- + MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx + EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT + BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx + MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD + YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv + aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf + MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1 + qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX + 4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd + XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ + k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw + BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn + M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4 + Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f + 9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8= + -----END CERTIFICATE----- + + + -----BEGIN CERTIFICATE----- + MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV + BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW + aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN + MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG + A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE + CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB + AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs + Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL + t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID + AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw + FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B + Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j + HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc + DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op + Lvtc9mL1J1IXvmM= + -----END CERTIFICATE----- + + + + + diff --git a/module/module.prop b/module/module.prop index 6f03f429..a37022ca 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=playintegrityfix -name=Play Integrity Fix (STRONG) -version=v16.0 -versionCode=16000 +name=Play Integrity Fix +version=v16.3 +versionCode=16300 author=chiteroman -description=WORKING TEE = STRONG PASS | THANKS GOOGLE +description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8-15 updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json diff --git a/module/pif.json b/module/pif.json index 564df3f5..bb0780cb 100644 --- a/module/pif.json +++ b/module/pif.json @@ -11,5 +11,5 @@ "TYPE": "user", "TAGS": "release-keys", "SECURITY_PATCH": "2017-12-05", - "DEVICE_INITIAL_SDK_INT": "25" -} \ No newline at end of file + "DEVICE_INITIAL_SDK_INT": "24" +} diff --git a/update.json b/update.json index 6ae5102d..305fff3c 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v16.0", - "versionCode": 16000, - "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v16.0/PlayIntegrityFix_v16.0.zip", + "version": "v16.3", + "versionCode": 16300, + "zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v16.3/PlayIntegrityFix_v16.3.zip", "changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md" }