diff --git a/module/src/main/cpp/zygisk/main.cpp b/module/src/main/cpp/zygisk/main.cpp index 89ff965..dc56b95 100644 --- a/module/src/main/cpp/zygisk/main.cpp +++ b/module/src/main/cpp/zygisk/main.cpp @@ -1,73 +1,24 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include "logging.hpp" #include "zygisk.hpp" #include "cJSON.h" +#define JSON_PATH "/data/adb/pif.json" +#define JSON_TS_PATH "/data/adb/tricky_store/pif.json" +#define JSON_PIF_PATH "/data/adb/playintegrityfix/pif.json" +#define JSON_PIF_FORK_PATH "/data/adb/modules/playintegrityfix/custom.pif.json" + using zygisk::Api; using zygisk::AppSpecializeArgs; using zygisk::ServerSpecializeArgs; -using namespace std::string_view_literals; -template struct FixedString { - // NOLINTNEXTLINE(*-explicit-constructor) - [[maybe_unused]] consteval inline FixedString(const char (&str)[N]) { - std::copy_n(str, N, data); - } - consteval inline FixedString() = default; - char data[N] = {}; -}; - -using PropValue = std::array; - -template -struct Prop { - using Type [[maybe_unused]] = T; - bool has_value{false}; - PropValue value {}; - - [[maybe_unused]] inline consteval static const char *getField() { - return Field.data; - } - [[maybe_unused]] inline consteval static bool isVersion() { - return Version; - } -}; - -static_assert(sizeof(Prop) % sizeof(void*) == 0); - -using SpoofConfig = std::tuple< - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop, - Prop ->; - - -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; + char *buf = (char *) buffer; while (count > 0) { ssize_t ret = read(fd, buf, count); if (ret < 0) return -1; @@ -78,9 +29,9 @@ ssize_t xread(int fd, void *buffer, size_t count) { return total; } -ssize_t xwrite(int fd, const void *buffer, size_t count) { +static ssize_t xwrite(int fd, const void *buffer, size_t count) { ssize_t total = 0; - char *buf = (char *)buffer; + char *buf = (char *) buffer; while (count > 0) { ssize_t ret = write(fd, buf, count); if (ret < 0) return -1; @@ -91,11 +42,6 @@ ssize_t xwrite(int fd, const void *buffer, size_t count) { return total; } -void trim(std::string_view &str) { - str.remove_prefix(std::min(str.find_first_not_of(" \t"), str.size())); - str.remove_suffix(std::min(str.size() - str.find_last_not_of(" \t") - 1, str.size())); -} - class TrickyStore : public zygisk::ModuleBase { public: void onLoad(Api *api, JNIEnv *env) override { @@ -105,65 +51,57 @@ public: void preAppSpecialize(AppSpecializeArgs *args) override { api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - if (args->app_data_dir == nullptr) { - return; - } - auto app_data_dir = env_->GetStringUTFChars(args->app_data_dir, nullptr); - auto nice_name = env_->GetStringUTFChars(args->nice_name, nullptr); + if (!args) return; - std::string_view dir(app_data_dir); - std::string_view process(nice_name); + const char *dir = env_->GetStringUTFChars(args->app_data_dir, nullptr); - bool isGms = false, isGmsUnstable = false; - isGms = dir.ends_with("/com.google.android.gms"); - isGmsUnstable = process == "com.google.android.gms.unstable"; + if (!dir) return; - env_->ReleaseStringUTFChars(args->app_data_dir, app_data_dir); - env_->ReleaseStringUTFChars(args->nice_name, nice_name); + bool isGms = std::string_view(dir).ends_with("/com.google.android.gms"); + + env_->ReleaseStringUTFChars(args->app_data_dir, dir); + + if (!isGms) return; - if (!isGms) { - return; - } api_->setOption(zygisk::FORCE_DENYLIST_UNMOUNT); - if (!isGmsUnstable) { + + const char *name = env_->GetStringUTFChars(args->nice_name, nullptr); + + if (!name) return; + + bool isGmsUnstable = std::string_view(name) == "com.google.android.gms.unstable"; + + env_->ReleaseStringUTFChars(args->nice_name, name); + + if (!isGmsUnstable) return; + + size_t jsonVectorSize; + std::vector temp; + + int fd = api_->connectCompanion(); + + xread(fd, &jsonVectorSize, sizeof(jsonVectorSize)); + temp.resize(jsonVectorSize); + xread(fd, temp.data(), jsonVectorSize); + + close(fd); + + if (temp.empty()) { + LOGI("Couldn't receive JSON file from fd!"); return; } - - int enabled = 0; - SpoofConfig spoofConfig{}; - auto fd = api_->connectCompanion(); - if (fd >= 0) [[likely]] { - // read enabled - xread(fd, &enabled, sizeof(enabled)); - if (enabled) { - xread(fd, &spoofConfig, sizeof(spoofConfig)); - } - close(fd); - } - if (enabled) { - LOGI("spoofing build vars in GMS!"); - auto buildClass = env_->FindClass("android/os/Build"); - auto buildVersionClass = env_->FindClass("android/os/Build$VERSION"); - std::apply([this, &buildClass, &buildVersionClass](auto &&... args) { - ((!args.has_value || - (setField::Type>( - std::remove_cvref_t::isVersion() ? buildVersionClass - : buildClass, - std::remove_cvref_t::getField(), - args.value) && - (LOGI("%s set %s to %s", - std::remove_cvref_t::isVersion() ? "VERSION" : "Build", - std::remove_cvref_t::getField(), - args.value.data()), true)) - ? void(0) - : LOGE("%s failed to set %s to %s", - std::remove_cvref_t::isVersion() ? "VERSION" : "Build", - std::remove_cvref_t::getField(), - args.value.data())), ...); - }, spoofConfig); - } + std::string jsonString(temp.cbegin(), temp.cend()); + json = cJSON_ParseWithLength(jsonString.c_str(), jsonVectorSize); + } + + void postAppSpecialize(const AppSpecializeArgs *args) override { + if (!json) return; + + UpdateBuildFields(); + + cJSON_Delete(json); } void preServerSpecialize(ServerSpecializeArgs *args) override { @@ -173,132 +111,105 @@ public: private: Api *api_{nullptr}; JNIEnv *env_{nullptr}; + cJSON *json{nullptr}; - template - inline bool setField(jclass clazz, const char* field, const PropValue& value); + void UpdateBuildFields() { + jclass buildClass = env_->FindClass("android/os/Build"); + jclass versionClass = env_->FindClass("android/os/Build$VERSION"); - template<> - inline bool setField(jclass clazz, const char* field, const PropValue& value) { - auto id = env_->GetStaticFieldID(clazz, field, "Ljava/lang/String;"); - if (!id) return false; - env_->SetStaticObjectField(clazz, id, env_->NewStringUTF(value.data())); - return true; - } + cJSON *currentElement = nullptr; + cJSON_ArrayForEach(currentElement, json) { + const char *key = currentElement->string; - template<> - inline bool setField(jclass clazz, const char* field, const PropValue& value) { - auto id = env_->GetStaticFieldID(clazz, field, "I"); - if (!id) return false; - char *p = nullptr; - jint x = static_cast(strtol(value.data(), &p, 10)); - if (p == value.data()) { - return false; + if (cJSON_IsString(currentElement)) { + const char *value = currentElement->valuestring; + jfieldID fieldID = env_->GetStaticFieldID(buildClass, key, "Ljava/lang/String;"); + + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + + fieldID = env_->GetStaticFieldID(versionClass, key, "Ljava/lang/String;"); + + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + continue; + } + } + + if (fieldID != nullptr) { + jstring jValue = env_->NewStringUTF(value); + + env_->SetStaticObjectField(buildClass, fieldID, jValue); + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + continue; + } + + LOGI("Set '%s' to '%s'", key, value); + } + } else if (cJSON_IsNumber(currentElement)) { + int value = currentElement->valueint; + jfieldID fieldID = env_->GetStaticFieldID(buildClass, key, "I"); + + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + + fieldID = env_->GetStaticFieldID(versionClass, key, "I"); + + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + continue; + } + } + + if (fieldID != nullptr) { + env_->SetStaticIntField(buildClass, fieldID, value); + + if (env_->ExceptionCheck()) { + env_->ExceptionClear(); + continue; + } + + LOGI("Set '%s' to '%d'", key, value); + } + } } - env_->SetStaticIntField(clazz, id, x); - return true; - } - - template<> - inline bool setField(jclass clazz, const char* field, const PropValue& value) { - auto id = env_->GetStaticFieldID(clazz, field, "Z"); - if (!id) return false; - auto x = std::string_view(value.data()); - if (x == "1" || x == "true") { - env_->SetStaticBooleanField(clazz, id, JNI_TRUE); - } else if (x == "0" || x == "false") { - env_->SetStaticBooleanField(clazz, id, JNI_FALSE); - } else { - return false; - } - return true; } }; -void read_config(FILE *config, SpoofConfig &spoof_config) { - char *l = nullptr; - struct finally { - char *(&l); +static std::vector readFile(const char *str) { + FILE *file = fopen(str, "r"); - ~finally() { free(l); } - } finally{l}; - size_t len = 0; - ssize_t n; - while ((n = getline(&l, &len, config)) != -1) { - if (n <= 1) continue; - std::string_view line{l, static_cast(n)}; - if (line.back() == '\n') { - line.remove_suffix(1); - } - auto d = line.find_first_of('='); - if (d == std::string_view::npos) { - LOGW("Ignore invalid line %.*s", static_cast(line.size()), line.data()); - continue; - } - auto key = line.substr(0, d); - trim(key); - auto value = line.substr(d + 1); - trim(value); - std::apply([&key, &value](auto &&... args) { - ((key == std::remove_cvref_t::getField() && - (LOGD("Read config: %.*s = %.*s", static_cast(key.size()), key.data(), - static_cast(value.size()), value.data()), - args.value.size() >= value.size() + 1 ? - (args.has_value = true, - strlcpy(args.value.data(), value.data(), - std::min(args.value.size(), value.size() + 1))) : - (LOGW("Config value %.*s for %.*s is too long, ignored", - static_cast(value.size()), value.data(), - static_cast(key.size()), key.data()), true))) || ...); - }, spoof_config); - } + if (!file) return {}; + + auto fileSize = std::filesystem::file_size(str); + + std::vector vector(fileSize); + + fread(vector.data(), fileSize, 1, file); + + fclose(file); + + LOGI("[companion_handler] file '%s', size: %ld", str, fileSize); + + return vector; } static void companion_handler(int fd) { - constexpr auto kSpoofConfigFile = "/data/adb/tricky_store/spoof_build_vars"sv; - constexpr auto kDefaultSpoofConfig = -R"EOF(MANUFACTURER=Google -MODEL=Pixel 8 Pro -FINGERPRINT=google/husky_beta/husky:15/AP31.240617.010/12136053:user/release-keys -BRAND=google -PRODUCT=husky_beta -DEVICE=husky -RELEASE=15 -ID=AP31.240617.010 -INCREMENTAL=12136053 -TYPE=user -TAGS=release-keys -SECURITY_PATCH=2024-07-05 -)EOF"sv; - struct stat st{}; - int enabled = stat(kSpoofConfigFile.data(), &st) == 0; - xwrite(fd, &enabled, sizeof(enabled)); + std::vector jsonVector; - if (!enabled) { - return; - } + jsonVector = readFile(JSON_PATH); + if (jsonVector.empty()) jsonVector = readFile(JSON_TS_PATH); + if (jsonVector.empty()) jsonVector = readFile(JSON_PIF_PATH); + if (jsonVector.empty()) jsonVector = readFile(JSON_PIF_FORK_PATH); - int cfd = -1; - if (st.st_size == 0) { - cfd = open(kSpoofConfigFile.data(), O_RDWR); - if (cfd > 0) { - xwrite(cfd, kDefaultSpoofConfig.data(), kDefaultSpoofConfig.size()); - lseek(cfd, 0, SEEK_SET); - } - } else { - cfd = open(kSpoofConfigFile.data(), O_RDONLY); - } - if (cfd < 0) { - LOGE("[companion_handler] Failed to open spoof_build_vars"); - return; - } + size_t jsonVectorSize = jsonVector.size(); - SpoofConfig spoof_config{}; - std::unique_ptr config{fdopen(cfd, "r")}; - read_config(config.get(), spoof_config); - - xwrite(fd, &spoof_config, sizeof(spoof_config)); + xwrite(fd, &jsonVectorSize, sizeof(jsonVectorSize)); + xwrite(fd, jsonVector.data(), jsonVectorSize); } // Register our module class and the companion handler function REGISTER_ZYGISK_MODULE(TrickyStore) + REGISTER_ZYGISK_COMPANION(companion_handler)