From 9be33d95284f35e793f9bdfe1679eb0d8963e2a9 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 21 Jul 2024 01:02:03 +0800 Subject: [PATCH] Refine spoof build vars --- .gitmodules | 3 - README.md | 28 +- module/build.gradle.kts | 51 +--- module/src/main/cpp/CMakeLists.txt | 22 +- module/src/main/cpp/external/CMakeLists.txt | 2 - module/src/main/cpp/external/glaze | 1 - .../src/main/cpp/logging/include/logging.hpp | 4 +- module/src/main/cpp/zygisk/main.cpp | 279 ++++++++++++------ 8 files changed, 215 insertions(+), 175 deletions(-) delete mode 160000 module/src/main/cpp/external/glaze diff --git a/.gitmodules b/.gitmodules index ccd2f91..d3c1aea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "module/src/main/cpp/external/LSPlt"] path = module/src/main/cpp/external/LSPlt url = https://github.com/LSPosed/LSPlt -[submodule "module/src/main/cpp/external/glaze"] - path = module/src/main/cpp/external/glaze - url = https://github.com/stephenberry/glaze diff --git a/README.md b/README.md index 100f8a6..1336e68 100644 --- a/README.md +++ b/README.md @@ -51,21 +51,19 @@ edit your spoof config. Here is an example of spoof config: -```json -{ - "manufacturer": "Google", - "model": "Pixel", - "fingerprint": "google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys", - "brand": "google", - "product": "sailfish", - "device": "sailfish", - "release": "8.1.0", - "id": "OPM1.171019.011", - "incremental": "4448085", - "security_patch": "2017-12-05", - "type": "user", - "tags": "release-keys" -} +``` +MANUFACTURER=Google +MODEL=Pixel +FINGERPRINT=google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys +BRAND=google +PRODUCT=sailfish +DEVICE=sailfish +RELEASE=8.1.0 +ID=OPM1.171019.011 +INCREMENTAL=4448085 +TYPE=user +TAGS=release-keys +SECURITY_PATCH=2017-12-05 ``` ## Support TEE broken devices diff --git a/module/build.gradle.kts b/module/build.gradle.kts index 5eef58f..4e77b02 100644 --- a/module/build.gradle.kts +++ b/module/build.gradle.kts @@ -16,12 +16,6 @@ val commitHash: String by rootProject.extra val abiList: List by rootProject.extra val androidMinSdkVersion: Int by rootProject.extra -val releaseFlags = arrayOf( - "-O3", "-flto", - "-Wno-unused", "-Wno-unused-parameter", - "-Wl,--exclude-libs,ALL", "-Wl,-icf=all,--lto-O3", "-Wl,-s,-x,--gc-sections" -) - android { defaultConfig { ndk { @@ -43,45 +37,24 @@ android { cmaker { default { - val cmakeArgs = arrayOf( + arguments += arrayOf( "-DANDROID_STL=none", + "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", + "-DANDROID_ALLOW_UNDEFINED_SYMBOLS=ON", "-DMODULE_NAME=$moduleId", - ) - arguments += cmakeArgs - abiFilters("arm64-v8a", "x86_64") - } - buildTypes { - when (it.name) { - "release" -> { - cppFlags += releaseFlags - cFlags += releaseFlags - } - } - val commonFlags = arrayOf( - // Silent noisy warnings - "-Wno-reorder-ctor", - "-Wno-overloaded-virtual", - "-Wno-unused-function", - "-Wno-unused-but-set-variable", - "-Wno-unused-private-field", - "-Wno-missing-braces", - "-Wno-delete-non-abstract-non-virtual-dtor", - "-Wno-unused-variable", - "-Wno-sometimes-uninitialized", - "-Wno-logical-op-parentheses", - "-Wno-shift-count-overflow", - "-Wno-deprecated-declarations", - "-Wno-infinite-recursion", - "-Wno-format", - "-Wno-deprecated-volatile", - ) - cppFlags += commonFlags - cFlags += commonFlags + "-DCMAKE_CXX_STANDARD=23", + "-DCMAKE_C_STANDARD=23", + "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON", + "-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON", + "-DCMAKE_CXX_VISIBILITY_PRESET=hidden", + "-DCMAKE_C_VISIBILITY_PRESET=hidden", + ) + abiFilters(*abiList.toTypedArray()) } } dependencies { - compileOnly(libs.cxx) + implementation(libs.cxx) } androidComponents.onVariants { variant -> diff --git a/module/src/main/cpp/CMakeLists.txt b/module/src/main/cpp/CMakeLists.txt index ff849c6..fe5b0c1 100644 --- a/module/src/main/cpp/CMakeLists.txt +++ b/module/src/main/cpp/CMakeLists.txt @@ -1,24 +1,9 @@ cmake_minimum_required(VERSION 3.28) -project(sample) - -if (CCACHE) - set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) - set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) -endif () +project(trick_store) find_package(cxx REQUIRED CONFIG) link_libraries(cxx::cxx) -find_program(CCACHE ccache) - -set(LINKER_FLAGS "-ffixed-x18 -Wl,--hash-style=both") - -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") - -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") - add_library(elf_util STATIC elf_util/elf_util.cpp) add_library(my_logging STATIC logging/logging.cpp) @@ -32,7 +17,6 @@ target_link_libraries(elf_util lsplt my_logging) # libutils stub add_library(utils SHARED binder/stub_utils.cpp) -target_compile_options(utils PRIVATE -fvisibility=default -fno-visibility-inlines-hidden) target_include_directories(utils PUBLIC binder/include) # libbinder stub @@ -42,11 +26,9 @@ target_link_libraries(binder PRIVATE utils) add_executable(libinject.so inject/main.cpp inject/utils.cpp) target_link_libraries(libinject.so PRIVATE lsplt my_logging) -target_compile_options(libinject.so PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden) add_library(${MODULE_NAME} SHARED binder_interceptor.cpp) target_link_libraries(${MODULE_NAME} PRIVATE log binder utils elf_util my_logging) -target_compile_options(${MODULE_NAME} PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden) add_library(tszygisk SHARED zygisk/main.cpp) -target_link_libraries(tszygisk PRIVATE log my_logging glaze::glaze) +target_link_libraries(tszygisk PRIVATE log my_logging) diff --git a/module/src/main/cpp/external/CMakeLists.txt b/module/src/main/cpp/external/CMakeLists.txt index 0cbf52d..2eced88 100644 --- a/module/src/main/cpp/external/CMakeLists.txt +++ b/module/src/main/cpp/external/CMakeLists.txt @@ -8,5 +8,3 @@ target_include_directories(lsplt PUBLIC LSPlt/lsplt/src/main/jni/include) target_include_directories(lsplt PRIVATE LSPlt/lsplt/src/main/jni) target_link_libraries(lsplt PUBLIC my_logging) # end lsplt - -add_subdirectory(glaze) diff --git a/module/src/main/cpp/external/glaze b/module/src/main/cpp/external/glaze deleted file mode 160000 index c365ba4..0000000 --- a/module/src/main/cpp/external/glaze +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c365ba48fb839f13290f1e4fc2a1aeab7a295438 diff --git a/module/src/main/cpp/logging/include/logging.hpp b/module/src/main/cpp/logging/include/logging.hpp index fecb958..d6426a3 100644 --- a/module/src/main/cpp/logging/include/logging.hpp +++ b/module/src/main/cpp/logging/include/logging.hpp @@ -13,8 +13,8 @@ #define LOGD(...) logging::log(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGV(...) logging::log(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #else -#define LOGD(...) -#define LOGV(...) +#define LOGD(...) (void)0 +#define LOGV(...) (void)0 #endif #define LOGI(...) logging::log(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGW(...) logging::log(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) diff --git a/module/src/main/cpp/zygisk/main.cpp b/module/src/main/cpp/zygisk/main.cpp index ee4c0e3..b48e846 100644 --- a/module/src/main/cpp/zygisk/main.cpp +++ b/module/src/main/cpp/zygisk/main.cpp @@ -1,11 +1,15 @@ -#include -#include -#include #include +#include +#include +#include +#include +#include #include +#include +#include +#include #include -#include "glaze/glaze.hpp" #include "logging.hpp" #include "zygisk.hpp" @@ -14,21 +18,51 @@ using zygisk::AppSpecializeArgs; using zygisk::ServerSpecializeArgs; using namespace std::string_view_literals; -struct spoof_config { - std::string manufacturer{"Google"}; - std::string model{"Pixel"}; - std::string fingerprint{"google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys"}; - std::string brand{"google"}; - std::string product{"sailfish"}; - std::string device{"sailfish"}; - std::string release{"8.1.0"}; - std::string id{"OPM1.171019.011"}; - std::string incremental{"4448085"}; - std::string security_patch{"2017-12-05"}; - std::string type{"user"}; - std::string tags{"release-keys"}; +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) { ssize_t total = 0; @@ -43,7 +77,7 @@ ssize_t xread(int fd, void *buffer, size_t count) { return total; } -ssize_t xwrite(int fd, void *buffer, size_t count) { +ssize_t xwrite(int fd, const void *buffer, size_t count) { ssize_t total = 0; char *buf = (char *)buffer; while (count > 0) { @@ -56,6 +90,11 @@ ssize_t xwrite(int fd, 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 { @@ -66,27 +105,15 @@ public: void preAppSpecialize(AppSpecializeArgs *args) override { api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); int enabled = 0; - spoof_config spoofConfig{}; - { - auto fd = api_->connectCompanion(); - if (fd >= 0) [[likely]] { - // read enabled - xread(fd, &enabled, sizeof(enabled)); - if (enabled) { - size_t bufferSize = 0; - std::string buffer; - // read size first - xread(fd, &bufferSize, sizeof(bufferSize)); - // resize and receive - buffer.resize(bufferSize); - xread(fd, buffer.data(), bufferSize); - // parse - if (glz::read_json(spoofConfig, buffer)) [[unlikely]] { - LOGE("[preAppSpecialize] spoofConfig parse error"); - } - } - close(fd); + 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) return; @@ -106,18 +133,21 @@ public: auto buildClass = env_->FindClass("android/os/Build"); auto buildVersionClass = env_->FindClass("android/os/Build$VERSION"); - setField(buildClass, "MANUFACTURER", std::move(spoofConfig.manufacturer)); - setField(buildClass, "MODEL", std::move(spoofConfig.model)); - setField(buildClass, "FINGERPRINT", std::move(spoofConfig.fingerprint)); - setField(buildClass, "BRAND", std::move(spoofConfig.brand)); - setField(buildClass, "PRODUCT", std::move(spoofConfig.product)); - setField(buildClass, "DEVICE", std::move(spoofConfig.device)); - setField(buildVersionClass, "RELEASE", std::move(spoofConfig.release)); - setField(buildClass, "ID", std::move(spoofConfig.id)); - setField(buildVersionClass, "INCREMENTAL", std::move(spoofConfig.incremental)); - setField(buildVersionClass, "SECURITY_PATCH", std::move(spoofConfig.security_patch)); - setField(buildClass, "TYPE", std::move(spoofConfig.type)); - setField(buildClass, "TAGS", std::move(spoofConfig.tags)); + 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("Failed to set %s to %s", std::remove_cvref_t::getField(), + args.value.data())), ...); + }, spoofConfig); } env_->ReleaseStringUTFChars(args->nice_name, nice_name); @@ -129,63 +159,126 @@ public: } private: - Api *api_; - JNIEnv *env_; + Api *api_{nullptr}; + JNIEnv *env_{nullptr}; - inline void setField(jclass clazz, const char* field, std::string&& value) { + template + inline bool setField(jclass clazz, const char* field, const PropValue& value); + + template<> + inline bool setField(jclass clazz, const char* field, const PropValue& value) { auto id = env_->GetStaticFieldID(clazz, field, "Ljava/lang/String;"); - env_->SetStaticObjectField(clazz, id, env_->NewStringUTF(value.c_str())); + if (!id) return false; + env_->SetStaticObjectField(clazz, id, env_->NewStringUTF(value.data())); + return true; + } + + 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; + } + 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; } }; -static inline void write_spoof_configs(const struct spoof_config& spoofConfig) { - std::string buffer{}; - - if (glz::write(spoofConfig, buffer)) [[unlikely]] { - // This should NEVER happen, but it's not the reason we don't handle the case - LOGE("[write_spoof_configs] Failed to parse json to std::string"); - return; - } - - // Remove old one first - std::filesystem::remove("/data/adb/tricky_store/spoof_build_vars"sv); - FILE* file = fopen("/data/adb/tricky_store/spoof_build_vars", "w"); - if (!file) [[unlikely]] { - LOGE("[write_spoof_configs] Failed to open spoof_build_vars"); - return; - } - - if (fprintf(file, "%s", buffer.c_str()) < 0) [[unlikely]] { - LOGE("[write_spoof_configs] Failed to write spoof_build_vars"); - fclose(file); - return; - } - - fclose(file); - LOGI("[write_spoof_configs] write done!"); -} - static void companion_handler(int fd) { - int enabled = access("/data/adb/tricky_store/spoof_build_vars", F_OK) == 0; + constexpr auto kSpoofConfigFile = "/data/adb/tricky_store/spoof_build_vars"sv; + constexpr auto kDefaultSpoofConfig = +R"EOF(MANUFACTURER=Google +MODEL=Pixel +FINGERPRINT=google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys +BRAND=google +PRODUCT=sailfish +DEVICE=sailfish +RELEASE=8.1.0 +ID=OPM1.171019.011 +INCREMENTAL=4448085 +TYPE=user +TAGS=release-keys +SECURITY_PATCH=2017-12-05 +)EOF"sv; + struct stat st{}; + int enabled = stat(kSpoofConfigFile.data(), &st) == 0; xwrite(fd, &enabled, sizeof(enabled)); if (!enabled) { return; } - spoof_config spoofConfig{}; - auto ec = glz::read_file_json(spoofConfig, "/data/adb/tricky_store/spoof_build_vars"sv, std::string{}); - if (ec) [[unlikely]] { - LOGW("[companion_handler] Failed to parse spoof_build_vars, writing and using default spoof config..."); - write_spoof_configs(spoofConfig); + int cfd = -1; + if (st.st_size == 0) { + cfd = open(kSpoofConfigFile.data(), O_RDWR); + if (cfd > 0) { + xwrite(fd, 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; + } + SpoofConfig spoof_config{}; + + { + std::unique_ptr config {fdopen(cfd, "r")}; + char *l = nullptr; + struct finally { char *l; ~finally() { free(l); } } finally{l}; + size_t len = 0; + ssize_t n; + while ((n = getline(&l, &len, config.get())) != -1) { + if (n == 0) 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); + } } - std::string buffer = glz::write_json(spoofConfig).value_or(""); - size_t bufferSize = buffer.size(); - // Send buffer size first - xwrite(fd, &bufferSize, sizeof(bufferSize)); - // client resize string stl and receive buffer - xwrite(fd, buffer.data(), bufferSize); + xwrite(fd, &spoof_config, sizeof(spoof_config)); } // Register our module class and the companion handler function