diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java index 9e0dc4f8..c8a14d96 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java @@ -49,7 +49,5 @@ public class ConfigManager { public static native String getDataPathPrefix(); - public static native boolean isAppNeedHook(String appDataDir); - public static native String getModulesList(); } diff --git a/edxp-core/src/main/cpp/main/src/config_manager.cpp b/edxp-core/src/main/cpp/main/src/config_manager.cpp index f026fc72..e94c1305 100644 --- a/edxp-core/src/main/cpp/main/src/config_manager.cpp +++ b/edxp-core/src/main/cpp/main/src/config_manager.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "art/runtime/native/native_util.h" #include "config_manager.h" @@ -58,10 +59,10 @@ namespace edxp { } void ConfigManager::UpdateConfigPath(const uid_t user) { - if (last_user_ != user) { - LOGI("updating config data paths from %u to %u...", last_user_, user); - last_user_ = user; - } + if (LIKELY(last_user_ == user && instance_)) return; + + LOGI("updating config data paths from %u to %u...", last_user_, user); + last_user_ = user; data_path_prefix_ = use_prot_storage_ ? "/data/user_de" : "/data/user"; data_path_prefix_ /= std::to_string(last_user_); @@ -79,6 +80,10 @@ namespace edxp { no_module_log_enabled_ = fs::exists(GetConfigPath("disable_modules_log")); hidden_api_bypass_enabled_ = !fs::exists(GetConfigPath("disable_hidden_api_bypass")); + modules_list_.clear(); + app_modules_list_.clear(); + + UpdateModuleList(); // use_white_list snapshot use_white_list_snapshot_ = fs::exists(use_whitelist_path_); @@ -95,36 +100,23 @@ namespace edxp { } } - std::tuple - ConfigManager::GetAppInfoFromDir(const std::string &app_data_dir) { - uid_t uid = 0; - fs::path path(app_data_dir); - std::vector splits(path.begin(), path.end()); - if (splits.size() < 5u) { - LOGE("can't parse %s", path.c_str()); - return {false, uid, {}}; + std::string ConfigManager::GetPackageNameFromBaseApkPath(const fs::path &path) { + std::vector paths(path.begin(), path.end()); + auto base_apk = paths.back(); // base.apk + if (base_apk != "base.apk") return {}; + paths.pop_back(); + auto pkg_name_with_obfuscation = paths.back(); + if (auto pos = pkg_name_with_obfuscation.find('-'); pos != std::string::npos) { + return pkg_name_with_obfuscation.substr(0, pos); } - const auto &uid_str = splits[3]; - const auto &package_name = splits[4]; - try { - uid = stol(uid_str); - } catch (const std::invalid_argument &ignored) { - LOGE("can't parse %s", app_data_dir.c_str()); - return {false, 0, {}}; - } - return {true, uid, package_name}; + return {}; } // TODO ignore unrelated processes - bool ConfigManager::IsAppNeedHook(const std::string &app_data_dir) { + bool ConfigManager::IsAppNeedHook(const uid_t user, const std::string &package_name) { // zygote always starts with `uid == 0` and then fork into different user. // so we have to check if we are the correct user or not. - const auto[res, user, package_name] = GetAppInfoFromDir(app_data_dir); - if (!res) return true; - - if (last_user_ != user) { - UpdateConfigPath(user); - } + UpdateConfigPath(user); if (!black_white_list_enabled_) { return true; @@ -135,7 +127,7 @@ namespace edxp { use_white_list = fs::exists(use_whitelist_path_); } else { LOGE("can't access config path, using snapshot use_white_list: %s", - app_data_dir.c_str()); + package_name.c_str()); use_white_list = use_white_list_snapshot_; } if (package_name == kPrimaryInstallerPkgName @@ -146,22 +138,22 @@ namespace edxp { if (use_white_list) { if (!can_access_app_data) { LOGE("can't access config path, using snapshot white list: %s", - app_data_dir.c_str()); + package_name.c_str()); return white_list_default_.count(package_name); } std::string target_path = whitelist_path_ / package_name; bool res = fs::exists(target_path); - LOGD("using whitelist, %s -> %d", app_data_dir.c_str(), res); + LOGD("using whitelist, %s -> %d", package_name.c_str(), res); return res; } else { if (!can_access_app_data) { LOGE("can't access config path, using snapshot black list: %s", - app_data_dir.c_str()); + package_name.c_str()); return black_list_default_.count(package_name); } std::string target_path = blacklist_path_ / package_name; bool res = !fs::exists(target_path); - LOGD("using blacklist, %s -> %d", app_data_dir.c_str(), res); + LOGD("using blacklist, %s -> %d", package_name.c_str(), res); return res; } } @@ -173,8 +165,9 @@ namespace edxp { } bool ConfigManager::UpdateModuleList() { - if (LIKELY(modules_list_) && !IsDynamicModulesEnabled()) + if (LIKELY(!modules_list_.empty()) && !IsDynamicModulesEnabled()) return true; + modules_list_.clear(); auto global_modules_list = GetConfigPath("modules.list"); if (!fs::exists(global_modules_list)) { LOGE("Cannot access path %s", global_modules_list.c_str()); @@ -193,9 +186,42 @@ namespace edxp { LOGE("Cannot access path %s", global_modules_list.c_str()); return false; } - modules_list_ = std::make_unique(std::istreambuf_iterator(ifs), - std::istreambuf_iterator()); + std::string module; + while (std::getline(ifs, module)) { + const auto &module_pkg_name = GetPackageNameFromBaseApkPath(module); + modules_list_.emplace_back(std::move(module), std::unordered_set{}); + const auto &module_scope_conf = GetConfigPath(module_pkg_name + ".conf"); + if (!fs::exists(module_scope_conf)) { + LOGD("module scope is not set for %s", module_pkg_name.c_str()); + continue; + } + std::ifstream ifs_c(module_scope_conf); + if (!ifs_c.good()) { + LOGE("Cannot access path %s", module_scope_conf.c_str()); + continue; + } + auto & scope = modules_list_.back().second; + std::string app_pkg_name; + while(std::getline(ifs_c, app_pkg_name)) { + scope.emplace(std::move(app_pkg_name)); + } + scope.insert(module_pkg_name); // Always add module itself + LOGD("scope of %s is:\n%s", module_pkg_name.c_str(), ([&scope](){ + std::ostringstream join; + std::copy(scope.begin(), scope.end(), std::ostream_iterator(join, "\n")); + return join.str(); + })().c_str()); + } return true; } + bool ConfigManager::UpdateAppModuleList(const uid_t user, const std::string &pkg_name) { + UpdateConfigPath(user); + app_modules_list_.clear(); + for(const auto&[module, scope]: modules_list_) { + if(scope.empty() || scope.count(pkg_name)) app_modules_list_.push_back(module); + } + return !app_modules_list_.empty(); + } + } \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/config_manager.h b/edxp-core/src/main/cpp/main/src/config_manager.h index 543518af..398e5bb6 100644 --- a/edxp-core/src/main/cpp/main/src/config_manager.h +++ b/edxp-core/src/main/cpp/main/src/config_manager.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace edxp { @@ -17,7 +18,6 @@ namespace edxp { class ConfigManager { public: - inline static ConfigManager *GetInstance() { if (!instance_) { instance_ = std::make_unique(); @@ -55,18 +55,17 @@ namespace edxp { return data_path_prefix_ / installer_pkg_name_ / "conf" / suffix; } - inline auto GetModulesList() const { return modules_list_.get(); } + inline auto GetAppModulesList() const { return app_modules_list_; }; - bool IsAppNeedHook(const std::string &app_data_dir); + bool UpdateAppModuleList(const uid_t user, const std::string &pkg_name); + + bool IsAppNeedHook(const uid_t user, const std::string &pkg_name); bool UpdateModuleList(); - static std::tuple - GetAppInfoFromDir(const std::string &app_data_dir); - private: inline static std::unique_ptr instance_ = nullptr; - uid_t last_user_ = false; + uid_t last_user_ = 0; bool use_prot_storage_ = true; std::filesystem::path data_path_prefix_; std::filesystem::path installer_pkg_name_; @@ -85,7 +84,9 @@ namespace edxp { std::unordered_set black_list_default_; bool hidden_api_bypass_enabled_ = false; - std::unique_ptr modules_list_ = nullptr; + std::vector>> modules_list_; + + std::vector app_modules_list_; std::filesystem::file_time_type last_write_time_; @@ -97,7 +98,11 @@ namespace edxp { std::string RetrieveInstallerPkgName() const; + static std::string GetPackageNameFromBaseApkPath(const std::filesystem::path &path); + + friend std::unique_ptr std::make_unique(); + }; } // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.cpp b/edxp-core/src/main/cpp/main/src/edxp_context.cpp index 77c9a57e..ac777773 100644 --- a/edxp-core/src/main/cpp/main/src/edxp_context.cpp +++ b/edxp-core/src/main/cpp/main/src/edxp_context.cpp @@ -23,6 +23,8 @@ #pragma clang diagnostic ignored "-Wunused-value" namespace edxp { + namespace fs = std::filesystem; + constexpr int FIRST_ISOLATED_UID = 99000; constexpr int LAST_ISOLATED_UID = 99999; constexpr int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; @@ -226,17 +228,31 @@ namespace edxp { jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) { - app_data_dir_ = env->NewStringUTF(SYSTEM_SERVER_DATA_DIR.c_str()); - ConfigManager::GetInstance()->UpdateModuleList(); + ConfigManager::GetInstance()->UpdateModuleList(); // I don't think we need this, but anyway + skip_ = false; + if (!ConfigManager::GetInstance()->IsAppNeedHook(0, "android")) { + skip_ = true; + LOGW("skip injecting xposed into android because it's whitelisted/blacklisted"); + } + if (!ConfigManager::GetInstance()->UpdateAppModuleList(0, "android")) { + skip_ = true; + LOGW("skip injecting into andorid because no module hooks it"); + } PreLoadDex(env, kInjectDexPath); } int Context::OnNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { - PrepareJavaEnv(env); - // only do work in child since FindAndCall would print log - FindAndCall(env, "forkSystemServerPost", "(I)V", res); + if (!skip_) { + PrepareJavaEnv(env); + // only do work in child since FindAndCall would print log + FindAndCall(env, "forkSystemServerPost", "(I)V", res); + } else { + auto config_manager = ConfigManager::ReleaseInstance(); + auto context = Context::ReleaseInstance(); + LOGD("skipped system server"); + } } else { // in zygote process, res is child zygote pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 @@ -244,28 +260,60 @@ namespace edxp { return 0; } + std::tuple + Context::GetAppInfoFromDir(JNIEnv *env, jstring dir) { + uid_t uid = 0; + JUTFString app_data_dir(env, dir); + if (!app_data_dir) return {false, 0, {}}; + fs::path path(app_data_dir.get()); + std::vector splits(path.begin(), path.end()); + if (splits.size() < 5u) { + LOGE("can't parse %s", path.c_str()); + return {false, uid, {}}; + } + const auto &uid_str = splits[3]; + const auto &package_name = splits[4]; + try { + uid = stol(uid_str); + } catch (const std::invalid_argument &ignored) { + LOGE("can't parse %s", app_data_dir.get()); + return {false, uid, {}}; + } + return {true, uid, package_name}; + } + bool Context::ShouldSkipInject(JNIEnv *env, jstring nice_name, jstring data_dir, jint uid, jboolean is_child_zygote) { const auto app_id = uid % PER_USER_RANGE; - const JUTFString package_name(env, nice_name, "UNKNOWN"); + const auto&[res, user, package_name] = GetAppInfoFromDir(env, data_dir); bool skip = false; - if (is_child_zygote) { + if (!res) { + LOGW("skip injecting into %s because it has no data dir", package_name.c_str()); skip = true; - LOGW("skip injecting into %s because it's a child zygote", package_name.get()); + } + if (!skip && is_child_zygote) { + skip = true; + LOGW("skip injecting into %s because it's a child zygote", package_name.c_str()); } - if ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || - (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || - app_id == SHARED_RELRO_UID) { + if (!skip && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || + (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && + app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || + app_id == SHARED_RELRO_UID)) { skip = true; - LOGW("skip injecting into %s because it's isolated", package_name.get()); + LOGW("skip injecting into %s because it's isolated", package_name.c_str()); } - const JUTFString dir(env, data_dir); - if (!dir || !ConfigManager::GetInstance()->IsAppNeedHook(dir)) { + if (!skip && !ConfigManager::GetInstance()->IsAppNeedHook(user, package_name)) { skip = true; LOGW("skip injecting xposed into %s because it's whitelisted/blacklisted", - package_name.get()); + package_name.c_str()); + } + + if (!skip && !ConfigManager::GetInstance()->UpdateAppModuleList(user, package_name)) { + skip = true; + LOGW("skip injecting xposed into %s because no module hooks it", + package_name.c_str()); } return skip; } @@ -283,9 +331,10 @@ namespace edxp { jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { + ConfigManager::GetInstance()->UpdateModuleList(); skip_ = ShouldSkipInject(env, nice_name, app_data_dir, uid, is_child_zygote); - ConfigManager::GetInstance()->UpdateModuleList(); + const JUTFString dir(env, app_data_dir, ""); app_data_dir_ = app_data_dir; nice_name_ = nice_name; PreLoadDex(env, kInjectDexPath); diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.h b/edxp-core/src/main/cpp/main/src/edxp_context.h index 5dcc5b94..a25a7bb9 100644 --- a/edxp-core/src/main/cpp/main/src/edxp_context.h +++ b/edxp-core/src/main/cpp/main/src/edxp_context.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "utils.h" @@ -113,8 +114,9 @@ namespace edxp { static bool ShouldSkipInject(JNIEnv *env, jstring nice_name, jstring data_dir, jint uid, jboolean is_child_zygote); - friend std::unique_ptr std::make_unique(); + static std::tuple GetAppInfoFromDir(JNIEnv *env, jstring dir); + friend std::unique_ptr std::make_unique(); }; } diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp index 6e8ba9e3..47a93832 100644 --- a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "edxp_config_manager.h" namespace edxp { @@ -54,21 +55,13 @@ namespace edxp { } - static jboolean ConfigManager_isAppNeedHook(JNI_START, jstring appDataDir) { - const char *app_data_dir = env->GetStringUTFChars(appDataDir, JNI_FALSE); - auto result = (jboolean) ConfigManager::GetInstance()->IsAppNeedHook(app_data_dir); - env->ReleaseStringUTFChars(appDataDir, app_data_dir); - return result; - } - static jstring ConfigManager_getModulesList(JNI_START) { - if (auto module_list = ConfigManager::GetInstance()->GetModulesList(); module_list) { - LOGD("module list: %s", module_list->c_str()); - return env->NewStringUTF(module_list->c_str()); - } else { - LOGW("Empty modules list"); - return env->NewStringUTF(""); - } + auto module_list = ConfigManager::GetInstance()->GetAppModulesList(); + std::ostringstream join; + std::copy(module_list.begin(), module_list.end(), std::ostream_iterator(join, "\n")); + const auto &list = join.str(); + LOGD("module list: %s", list.c_str()); + return env->NewStringUTF(list.c_str()); } static JNINativeMethod gMethods[] = { @@ -84,7 +77,6 @@ namespace edxp { NATIVE_METHOD(ConfigManager, getDataPathPrefix, "()Ljava/lang/String;"), NATIVE_METHOD(ConfigManager, getInstallerConfigPath, "(Ljava/lang/String;)Ljava/lang/String;"), - NATIVE_METHOD(ConfigManager, isAppNeedHook, "(Ljava/lang/String;)Z"), NATIVE_METHOD(ConfigManager, getModulesList, "()Ljava/lang/String;"), };