diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 87dc92b..a737280 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -53,9 +53,15 @@ static void modify_callback(void *cookie, const char *name, const char *value, u if (!cookie || !name || !value || !o_callback) return; + const char *oldValue = value; + std::string_view prop(name); - if (prop.ends_with("api_level")) { + if (prop == "init.svc.adbd") { + value = "stopped"; + } else if (prop == "sys.usb.state") { + value = "mtp"; + } else if (prop.ends_with("api_level")) { if (!DEVICE_INITIAL_SDK_INT.empty()) { value = DEVICE_INITIAL_SDK_INT.c_str(); } @@ -69,7 +75,11 @@ static void modify_callback(void *cookie, const char *name, const char *value, u } } - if (DEBUG) LOGD("[%s]: '%s'", name, value); + if (strcmp(oldValue, value) == 0) { + if (DEBUG) LOGD("[%s]: %s (unchanged)", name, oldValue); + } else { + LOGD("[%s]: %s -> %s", name, oldValue, value); + } return o_callback(cookie, name, value, serial); } @@ -82,12 +92,10 @@ static void my_system_property_read_callback(prop_info *pi, T_Callback callback, } static bool doHook() { - LOGD("loaded Dobby version: %s", DobbyGetVersion()); - void *ptr = DobbySymbolResolver(nullptr, "__system_property_read_callback"); - if (ptr && !DobbyHook(ptr, (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback)) { + if (ptr && DobbyHook(ptr, (void *) my_system_property_read_callback, + (void **) &o_system_property_read_callback) == 0) { LOGD("hook __system_property_read_callback successful at %p", ptr); return true; } @@ -166,6 +174,9 @@ public: bool trickyStore = false; xread(fd, &trickyStore, sizeof(trickyStore)); + bool testSignedRom = false; + xread(fd, &testSignedRom, sizeof(testSignedRom)); + close(fd); LOGD("Dex file size: %d", dexSize); @@ -178,6 +189,11 @@ public: spoofProvider = false; spoofProps = false; } + + if (testSignedRom) { + LOGD("--- ROM IS SIGNED WITH TEST KEYS ---"); + spoofSignature = true; + } } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { @@ -213,8 +229,8 @@ private: JNIEnv *env = nullptr; std::vector dexVector; nlohmann::json json; - bool spoofProps = false; - bool spoofProvider = false; + bool spoofProps = true; + bool spoofProvider = true; bool spoofSignature = false; void dlclose() { @@ -225,14 +241,6 @@ private: void parseJSON() { if (json.empty()) return; - if (json.contains("ID") && json["ID"].is_string()) { - BUILD_ID = json["ID"].get(); - } - - if (json.contains("SECURITY_PATCH") && json["SECURITY_PATCH"].is_string()) { - SECURITY_PATCH = json["SECURITY_PATCH"].get(); - } - if (json.contains("DEVICE_INITIAL_SDK_INT")) { if (json["DEVICE_INITIAL_SDK_INT"].is_string()) { DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get(); @@ -246,18 +254,57 @@ private: if (json.contains("spoofProvider") && json["spoofProvider"].is_boolean()) { spoofProvider = json["spoofProvider"].get(); + json.erase("spoofProvider"); } if (json.contains("spoofProps") && json["spoofProps"].is_boolean()) { spoofProps = json["spoofProps"].get(); + json.erase("spoofProps"); } if (json.contains("spoofSignature") && json["spoofSignature"].is_boolean()) { spoofSignature = json["spoofSignature"].get(); + json.erase("spoofSignature"); } if (json.contains("DEBUG") && json["DEBUG"].is_boolean()) { DEBUG = json["DEBUG"].get(); + json.erase("DEBUG"); + } + + if (json.contains("FINGERPRINT") && json["FINGERPRINT"].is_string()) { + std::string fingerprint = json["FINGERPRINT"].get(); + + std::vector vector; + auto parts = fingerprint | std::views::split('/'); + + for (const auto &part: parts) { + auto subParts = std::string(part.begin(), part.end()) | std::views::split(':'); + for (const auto &subPart: subParts) { + vector.emplace_back(subPart.begin(), subPart.end()); + } + } + + if (vector.size() == 8) { + json["BRAND"] = vector[0]; + json["PRODUCT"] = vector[1]; + json["DEVICE"] = vector[2]; + json["RELEASE"] = vector[3]; + json["ID"] = vector[4]; + json["INCREMENTAL"] = vector[5]; + json["TYPE"] = vector[6]; + json["TAGS"] = vector[7]; + } else { + LOGE("Error parsing fingerprint values!"); + } + } + + if (json.contains("SECURITY_PATCH") && json["SECURITY_PATCH"].is_string()) { + SECURITY_PATCH = json["SECURITY_PATCH"].get(); + } + + if (json.contains("ID") && json["ID"].is_string()) { + BUILD_ID = json["ID"].get(); } } @@ -378,6 +425,26 @@ static std::vector readFile(const char *path) { return vector; } +static bool checkOtaZip() { + std::array buffer{}; + std::string result; + bool found = false; + + std::unique_ptr pipe( + popen("unzip -l /system/etc/security/otacerts.zip", "r"), pclose); + if (!pipe) return false; + + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + if (result.find("test") != std::string::npos) { + found = true; + break; + } + } + + return found; +} + static void companion(int fd) { std::vector dex, json; @@ -403,6 +470,9 @@ static void companion(int fd) { bool trickyStore = std::filesystem::exists(TS_PATH); xwrite(fd, &trickyStore, sizeof(trickyStore)); + + bool testSignedRom = checkOtaZip(); + xwrite(fd, &testSignedRom, sizeof(testSignedRom)); } REGISTER_ZYGISK_MODULE(PlayIntegrityFix) diff --git a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java index b48a1c0..0a5af36 100644 --- a/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java +++ b/app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java @@ -181,15 +181,16 @@ public final class EntryPoint { jsonObject.keys().forEachRemaining(key -> { Field field = getBuildField(key); if (field == null) return; - field.setAccessible(true); - String value; try { - value = jsonObject.getString(key); + String value = jsonObject.getString(key); + if (value.isBlank()) { + Log.w(TAG, "Field '" + key + "' have an empty value!"); + } else { + map.put(field, value); + } } catch (Throwable t) { Log.e(TAG, "init", t); - return; } - map.putIfAbsent(field, value); }); Log.i(TAG, "Parsed " + map.size() + " fields from JSON"); @@ -200,9 +201,14 @@ public final class EntryPoint { public static void spoofFields() { map.forEach((field, value) -> { try { + field.setAccessible(true); String oldValue = (String) field.get(null); - if (value.equals(oldValue)) return; + if (value.equals(oldValue)) { + field.setAccessible(false); + return; + } field.set(null, value); + field.setAccessible(false); Log.i(TAG, "Set '" + field.getName() + "' to '" + value + "'"); } catch (Throwable t) { Log.e(TAG, "spoofFields", t); diff --git a/module/pif.json b/module/pif.json index 368fe7d..21c7d74 100644 --- a/module/pif.json +++ b/module/pif.json @@ -1,19 +1,7 @@ { - "TYPE": "user", - "TAGS": "release-keys", - "ID": "AP41.240823.009", - "BRAND": "google", - "DEVICE": "tokay", "FINGERPRINT": "google/tokay_beta/tokay:15/AP41.240823.009/12329489:user/release-keys", "MANUFACTURER": "Google", "MODEL": "Pixel 9", - "PRODUCT": "tokay_beta", - "INCREMENTAL": "12329489", - "RELEASE": "15", "SECURITY_PATCH": "2024-09-05", - "DEVICE_INITIAL_SDK_INT": 25, - "spoofProvider": true, - "spoofProps": true, - "spoofSignature": false, - "DEBUG": false + "DEVICE_INITIAL_SDK_INT": 21 }