diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index aa851fd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 02c7cfd..4c659c5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,6 +14,7 @@ android { packaging { jniLibs { + excludes += "**/liblog.so" excludes += "**/libdobby.so" } } @@ -22,19 +23,22 @@ android { applicationId = "es.chiteroman.playintegrityfix" minSdk = 26 targetSdk = 34 - versionCode = 1 - versionName = "1.0" + versionCode = 15200 + versionName = "v15.2" externalNativeBuild { cmake { arguments += "-DANDROID_STL=none" arguments += "-DCMAKE_BUILD_TYPE=Release" + arguments += "-DCMAKE_CXX_STANDARD=20" + arguments += "-DCMAKE_CXX_STANDARD_REQUIRED=True" + arguments += "-DCMAKE_CXX_EXTENSIONS=False" + arguments += "-DCMAKE_CXX_VISIBILITY_PRESET=hidden" + arguments += "-DCMAKE_VISIBILITY_INLINES_HIDDEN=True" + arguments += "-DPlugin.Android.BionicLinkerUtil=ON" - cppFlags += "-std=c++20" cppFlags += "-fno-exceptions" cppFlags += "-fno-rtti" - cppFlags += "-fvisibility=hidden" - cppFlags += "-fvisibility-inlines-hidden" } } } @@ -64,7 +68,26 @@ dependencies { implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") } +tasks.register("updateModuleProp") { + doLast { + val versionName = project.android.defaultConfig.versionName + val versionCode = project.android.defaultConfig.versionCode + + val modulePropFile = project.rootDir.resolve("module/module.prop") + + var content = modulePropFile.readText() + + content = content.replace(Regex("version=.*"), "version=$versionName") + content = content.replace(Regex("versionCode=.*"), "versionCode=$versionCode") + + modulePropFile.writeText(content) + } +} + + tasks.register("copyFiles") { + dependsOn("updateModuleProp") + doLast { val moduleFolder = project.rootDir.resolve("module") val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex") @@ -83,12 +106,12 @@ tasks.register("copyFiles") { tasks.register("zip") { dependsOn("copyFiles") - archiveFileName.set("PlayIntegrityFix.zip") + archiveFileName.set("PlayIntegrityFix_${project.android.defaultConfig.versionName}.zip") destinationDirectory.set(project.rootDir.resolve("out")) from(project.rootDir.resolve("module")) } afterEvaluate { - tasks["assembleRelease"].finalizedBy("copyFiles", "zip") + tasks["assembleRelease"].finalizedBy("updateModuleProp", "copyFiles", "zip") } diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 4d5c343..75435e5 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.22.1) -project("playintegrityfix") +project(playintegrityfix) find_package(cxx REQUIRED CONFIG) @@ -10,6 +10,4 @@ add_library(${CMAKE_PROJECT_NAME} SHARED module.cpp) add_subdirectory(Dobby) -SET_OPTION(Plugin.Android.BionicLinkerUtil ON) - target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log dobby_static) \ No newline at end of file diff --git a/app/src/main/cpp/module.cpp b/app/src/main/cpp/module.cpp index 68c0ffb..be8ad97 100644 --- a/app/src/main/cpp/module.cpp +++ b/app/src/main/cpp/module.cpp @@ -1,7 +1,8 @@ #include #include #include - +#include +#include #include "zygisk.hpp" #include "dobby.h" #include "json.hpp" @@ -14,15 +15,17 @@ #define PIF_JSON_2 "/data/adb/modules/playintegrityfix/pif.json" -static std::string FIRST_API_LEVEL, SECURITY_PATCH; +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 || o_callback == nullptr) return; + if (cookie == nullptr || name == nullptr || value == nullptr || + !callbacks.contains(cookie)) + return; std::string_view prop(name); @@ -40,6 +43,13 @@ static void modify_callback(void *cookie, const char *name, const char *value, u value = FIRST_API_LEVEL.c_str(); } + } else if (prop.ends_with("build.id")) { + + if (!BUILD_ID.empty()) { + + value = BUILD_ID.c_str(); + } + } else if (prop == "sys.usb.state") { value = "none"; @@ -50,7 +60,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 *); @@ -60,12 +70,12 @@ 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(nullptr, "__system_property_read_callback"); + void *handle = DobbySymbolResolver("libc.so", "__system_property_read_callback"); if (handle == nullptr) { LOGD("Couldn't find '__system_property_read_callback' handle. Report to @chiteroman"); return; @@ -87,68 +97,49 @@ public: void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - if (args->is_child_zygote && *args->is_child_zygote) { + auto name = env->GetStringUTFChars(args->nice_name, nullptr); + + if (name == nullptr) { api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); return; } - auto name = env->GetStringUTFChars(args->nice_name, nullptr); + std::string_view process(name); - if (name && strncmp(name, "com.google.android.gms", 22) == 0) { + bool isGms = process.starts_with("com.google.android.gms"); + bool isGmsUnstable = process == "com.google.android.gms.unstable"; - api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); + env->ReleaseStringUTFChars(args->nice_name, name); - if (strcmp(name, "com.google.android.gms.unstable") == 0) { - - long dexSize = 0, jsonSize = 0; - int fd = api->connectCompanion(); - - read(fd, &dexSize, sizeof(long)); - - if (dexSize > 0) { - - dexVector.resize(dexSize); - read(fd, dexVector.data(), dexSize); - - } else { - - LOGD("Couldn't load classes.dex file in memory!"); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - goto end; - } - - read(fd, &jsonSize, sizeof(long)); - - if (jsonSize > 0) { - - jsonVector.resize(jsonSize); - read(fd, jsonVector.data(), jsonSize); - - } else { - - LOGD("Couldn't load pif.json file in memory!"); - dexVector.clear(); - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - goto end; - } - - end: - close(fd); - goto clear; - } + if (!isGms) { + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + return; } - api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - clear: - env->ReleaseStringUTFChars(args->nice_name, name); - } + if (!isGmsUnstable) { + api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + return; + } - void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - if (dexVector.empty() || jsonVector.empty()) return; + long dexSize = 0, jsonSize = 0; - std::string jsonStr(jsonVector.cbegin(), jsonVector.cend()); - nlohmann::json json = nlohmann::json::parse(jsonStr, nullptr, false, true); + int fd = api->connectCompanion(); + + read(fd, &dexSize, sizeof(long)); + read(fd, &jsonSize, sizeof(long)); + + vector.resize(dexSize); + read(fd, vector.data(), dexSize); + + char jsonBufer[jsonSize]; + read(fd, jsonBufer, jsonSize); + + close(fd); + + std::string_view jsonStr(jsonBufer, jsonSize); + json = nlohmann::json::parse(jsonStr, nullptr, false, true); if (json.contains("FIRST_API_LEVEL")) { @@ -180,6 +171,29 @@ public: LOGD("JSON file doesn't contain SECURITY_PATCH key :("); } + if (json.contains("ID")) { + + if (json["ID"].is_string()) { + + BUILD_ID = json["ID"].get(); + } + + } else if (json.contains("BUILD_ID")) { + + if (json["BUILD_ID"].is_string()) { + + BUILD_ID = json["BUILD_ID"].get(); + } + + } else { + + LOGD("JSON file doesn't contain ID/BUILD_ID keys :("); + } + } + + void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { + if (vector.empty() || json.empty()) return; + doHook(); LOGD("get system classloader"); @@ -192,7 +206,7 @@ public: auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClInit = env->GetMethodID(dexClClass, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - auto buffer = env->NewDirectByteBuffer(dexVector.data(), dexVector.size()); + auto buffer = env->NewDirectByteBuffer(vector.data(), vector.size()); auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); LOGD("load class"); @@ -208,8 +222,8 @@ public: auto str = env->NewStringUTF(json.dump().c_str()); env->CallStaticVoidMethod(entryClass, entryInit, str); - dexVector.clear(); - jsonVector.clear(); + vector.clear(); + json.clear(); } void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { @@ -219,50 +233,43 @@ public: private: zygisk::Api *api = nullptr; JNIEnv *env = nullptr; - std::vector dexVector, jsonVector; + std::vector vector; + nlohmann::json json; }; static void companion(int fd) { - long dexSize = 0, jsonSize = 0; - std::vector dexVector, jsonVector; + std::ifstream dexFile(CLASSES_DEX, std::ios::in | std::ios::binary); - FILE *dexFile = fopen(CLASSES_DEX, "rb"); - - if (dexFile) { - - fseek(dexFile, 0, SEEK_END); - dexSize = ftell(dexFile); - fseek(dexFile, 0, SEEK_SET); - - dexVector.resize(dexSize); - fread(dexVector.data(), 1, dexSize, dexFile); - - fclose(dexFile); + if (!dexFile) { + long i = 0; + write(fd, &i, sizeof(i)); + return; } + std::vector dexVector((std::istreambuf_iterator(dexFile)), + std::istreambuf_iterator()); + long dexSize = dexVector.size(); + + std::ifstream jsonFile; + + if (std::filesystem::exists(PIF_JSON)) { + jsonFile = std::ifstream(PIF_JSON, std::ios::in); + } else if (std::filesystem::exists(PIF_JSON_2)) { + jsonFile = std::ifstream(PIF_JSON_2, std::ios::in); + } else { + long i = 0; + write(fd, &i, sizeof(i)); + return; + } + + std::vector jsonVector((std::istreambuf_iterator(jsonFile)), + std::istreambuf_iterator()); + long jsonSize = jsonVector.size(); + write(fd, &dexSize, sizeof(long)); - write(fd, dexVector.data(), dexSize); - - FILE *jsonFile = fopen(PIF_JSON, "r"); - - if (jsonFile == nullptr) { - - jsonFile = fopen(PIF_JSON_2, "r"); - } - - if (jsonFile) { - - fseek(jsonFile, 0, SEEK_END); - jsonSize = ftell(jsonFile); - fseek(jsonFile, 0, SEEK_SET); - - jsonVector.resize(jsonSize); - fread(jsonVector.data(), 1, jsonSize, jsonFile); - - fclose(jsonFile); - } - write(fd, &jsonSize, sizeof(long)); + + write(fd, dexVector.data(), dexSize); write(fd, jsonVector.data(), jsonSize); } diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java index 77fec53..3ec3fb4 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/CustomProvider.java @@ -14,8 +14,9 @@ public final class CustomProvider extends Provider { @Override public synchronized Service getService(String type, String algorithm) { + EntryPoint.LOG(String.format("[Service] Type: '%s' | Algorithm: '%s'", type, algorithm)); - EntryPoint.spoofDevice(); + if ("KeyStore".equals(type)) EntryPoint.spoofDevice(); return super.getService(type, algorithm); } diff --git a/module/module.prop b/module/module.prop index b999caa..aed930b 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=playintegrityfix name=Play Integrity Fix -version=v15.1 -versionCode=15100 +version=v15.2 +versionCode=15200 author=chiteroman description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8+. updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json diff --git a/module/pif.json b/module/pif.json index ae3dfb3..a154748 100644 --- a/module/pif.json +++ b/module/pif.json @@ -7,5 +7,6 @@ "FINGERPRINT": "acer/c01_ww/acer_c01:7.1.1/NMF26F/1521514970:user/release-keys", "SECURITY_PATCH": "2018-04-01", "FIRST_API_LEVEL": 24, + "ID": "NMF26F", "FORCE_BASIC_ATTESTATION": true }