Const ConfigManger

This commit is contained in:
LoveSy 2020-11-22 14:34:43 +08:00 committed by solohsu
parent 1e9b307f54
commit 8bde89c04c
11 changed files with 142 additions and 198 deletions

View File

@ -28,11 +28,6 @@ public class BaseEdxpConfig implements EdxpConfig {
return ConfigManager.getLibSandHookName();
}
@Override
public boolean isDynamicModulesMode() {
return ConfigManager.isDynamicModulesEnabled();
}
@Override
public boolean isResourcesHookEnabled() {
return ConfigManager.isResourcesHookEnabled();

View File

@ -29,8 +29,6 @@ public class ConfigManager {
public static native boolean isBlackWhiteListEnabled();
public static native boolean isDynamicModulesEnabled();
public static native boolean isNoModuleLogEnabled();
public static native boolean isResourcesHookEnabled();

View File

@ -10,6 +10,7 @@
#include "jni_env_ext.h"
#include "edxp_context.h"
#include "jni/edxp_pending_hooks.h"
#include "utils.h"
namespace art {

View File

@ -5,6 +5,7 @@
#include <string>
#include <filesystem>
#include <sys/system_properties.h>
#include "logging.h"
#include <sys/system_properties.h>
@ -20,12 +21,19 @@ namespace edxp {
return {str, size};
}
inline bool path_exists(const std::filesystem::path &path) {
inline bool path_exists(const std::filesystem::path &path, bool quite = false) {
try {
return std::filesystem::exists(path);
} catch (const std::filesystem::filesystem_error &e) {
LOGE("%s", e.what());
if (!quite)
LOGE("%s", e.what());
return false;
}
}
static inline int32_t GetAndroidApiLevel() {
char prop_value[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", prop_value);
return std::atoi(prop_value);
}
}

View File

@ -25,12 +25,12 @@ namespace edxp {
std::string ConfigManager::RetrieveInstallerPkgName() const {
std::string data_test_path = data_path_prefix_ / kPrimaryInstallerPkgName;
if (path_exists(data_test_path)) {
if (path_exists(data_test_path, true)) {
LOGI("using installer %s", kPrimaryInstallerPkgName.c_str());
return kPrimaryInstallerPkgName;
}
data_test_path = data_path_prefix_ / kLegacyInstallerPkgName;
if (path_exists(data_test_path)) {
if (path_exists(data_test_path, true)) {
LOGI("using installer %s", kLegacyInstallerPkgName.c_str());
return kLegacyInstallerPkgName;
}
@ -39,69 +39,14 @@ namespace edxp {
return kPrimaryInstallerPkgName;
}
void ConfigManager::SnapshotBlackWhiteList() {
white_list_default_.clear();
black_list_default_.clear();
try {
for (auto &item: fs::directory_iterator(whitelist_path_)) {
if (item.is_regular_file()) {
const auto &file_name = item.path().filename();
LOGI(" whitelist: %s", file_name.c_str());
white_list_default_.emplace(file_name);
}
std::unordered_set<std::string> ConfigManager::GetAppList(const fs::path &dir) {
std::unordered_set<std::string> set;
for (auto &item: fs::directory_iterator(dir)) {
if (item.is_regular_file()) {
set.emplace(item.path().filename());
}
for (auto &item: fs::directory_iterator(blacklist_path_)) {
if (item.is_regular_file()) {
const auto &file_name = item.path().filename();
LOGI(" blacklist: %s", file_name.c_str());
black_list_default_.emplace(file_name);
}
}
} catch (const fs::filesystem_error &e) {
LOGE("%s", e.what());
}
}
void ConfigManager::UpdateConfigPath(const uid_t 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_);
installer_pkg_name_ = RetrieveInstallerPkgName();
base_config_path_ = GetConfigPath("");
blacklist_path_ = GetConfigPath("blacklist/");
whitelist_path_ = GetConfigPath("whitelist/");
use_whitelist_path_ = GetConfigPath("usewhitelist");
dynamic_modules_enabled_ = path_exists(GetConfigPath("dynamicmodules"));
black_white_list_enabled_ = path_exists(GetConfigPath("blackwhitelist"));
deopt_boot_image_enabled_ = path_exists(GetConfigPath("deoptbootimage"));
resources_hook_enabled_ = path_exists(GetConfigPath("enable_resources"));
no_module_log_enabled_ = path_exists(GetConfigPath("disable_modules_log"));
hidden_api_bypass_enabled_ =
!path_exists(GetConfigPath("disable_hidden_api_bypass"));
modules_list_.clear();
app_modules_list_.clear();
UpdateModuleList();
// use_white_list snapshot
use_white_list_snapshot_ = path_exists(use_whitelist_path_);
LOGI("data path prefix: %s", data_path_prefix_.c_str());
LOGI(" application list mode: %s", BoolToString(black_white_list_enabled_));
LOGI(" using whitelist: %s", BoolToString(use_white_list_snapshot_));
LOGI(" dynamic modules mode: %s", BoolToString(dynamic_modules_enabled_));
LOGI(" resources hook: %s", BoolToString(resources_hook_enabled_));
LOGI(" deopt boot image: %s", BoolToString(deopt_boot_image_enabled_));
LOGI(" no module log: %s", BoolToString(no_module_log_enabled_));
LOGI(" hidden api bypass: %s", BoolToString(hidden_api_bypass_enabled_));
if (black_white_list_enabled_) {
SnapshotBlackWhiteList();
}
return set;
}
std::string ConfigManager::GetPackageNameFromBaseApkPath(const fs::path &path) {
@ -117,85 +62,71 @@ namespace edxp {
}
// TODO ignore unrelated processes
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.
UpdateConfigPath(user);
bool ConfigManager::IsAppNeedHook(const std::string &package_name) const {
if (!black_list_enable_ && !white_list_enable_) {
return true;
}
if (!black_white_list_enabled_) {
if (package_name == installer_pkg_name_) {
return true;
}
bool can_access_app_data = path_exists(base_config_path_);
bool use_white_list;
if (can_access_app_data) {
use_white_list = path_exists(use_whitelist_path_);
} else {
LOGE("can't access config path, using snapshot use_white_list: %s",
package_name.c_str());
use_white_list = use_white_list_snapshot_;
}
if (package_name == kPrimaryInstallerPkgName
|| package_name == kLegacyInstallerPkgName) {
// always hook installer apps
return true;
}
if (use_white_list) {
if (!can_access_app_data) {
LOGE("can't access config path, using snapshot white list: %s",
package_name.c_str());
return white_list_default_.count(package_name);
}
std::string target_path = whitelist_path_ / package_name;
bool res = path_exists(target_path);
if (white_list_enable_) {
auto res = white_list_.count(package_name);
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",
package_name.c_str());
return black_list_default_.count(package_name) == 0;
}
std::string target_path = blacklist_path_ / package_name;
bool res = !path_exists(target_path);
auto res = black_list_.count(package_name);
LOGD("using blacklist, %s -> %d", package_name.c_str(), res);
return res;
}
}
ConfigManager::ConfigManager() {
use_prot_storage_ = GetAndroidApiLevel() >= __ANDROID_API_N__;
last_user_ = 0;
UpdateConfigPath(last_user_);
ConfigManager::ConfigManager(uid_t user) :
user_(user),
data_path_prefix_(fs::path(use_prot_storage_ ? "/data/user_de" : "/data/user") /
std::to_string(user_)),
installer_pkg_name_(RetrieveInstallerPkgName()),
black_list_enable_(path_exists(GetConfigPath("blacklist"))),
white_list_enable_(path_exists(GetConfigPath("whiltelist"))),
deopt_boot_image_enabled_(path_exists(GetConfigPath("deoptbootimage"))),
no_module_log_enabled_(path_exists(GetConfigPath("disable_modules_log"))),
resources_hook_enabled_(path_exists(GetConfigPath("enable_resources"))),
hidden_api_bypass_enabled_(!path_exists(GetConfigPath("disable_hidden_api_bypass"))),
white_list_(white_list_enable_ ? GetAppList(GetConfigPath("whitelist/"))
: std::unordered_set<std::string>{}),
black_list_(black_list_enable_ ? GetAppList(GetConfigPath("blacklist/"))
: std::unordered_set<std::string>{}),
modules_list_(GetModuleList()),
last_write_time_(GetLastWriteTime()){
// use_white_list snapshot
LOGI("data path prefix: %s", data_path_prefix_.c_str());
LOGI(" using blacklist: %s", BoolToString(black_list_enable_));
LOGI(" using whitelist: %s", BoolToString(white_list_enable_));
LOGI(" resources hook: %s", BoolToString(resources_hook_enabled_));
LOGI(" deopt boot image: %s", BoolToString(deopt_boot_image_enabled_));
LOGI(" no module log: %s", BoolToString(no_module_log_enabled_));
LOGI(" hidden api bypass: %s", BoolToString(hidden_api_bypass_enabled_));
}
bool ConfigManager::UpdateModuleList() {
if (LIKELY(!modules_list_.empty()) && !IsDynamicModulesEnabled())
return true;
modules_list_.clear();
auto ConfigManager::GetModuleList() -> std::remove_const_t<decltype(modules_list_)> {
std::remove_const_t<decltype(modules_list_)> modules_list;
auto global_modules_list = GetConfigPath("modules.list");
if (!path_exists(global_modules_list)) {
LOGE("Cannot access path %s", global_modules_list.c_str());
return false;
return modules_list;
}
if (auto last_write_time = fs::last_write_time(global_modules_list);
LIKELY(last_write_time < last_write_time_)) {
return true;
} else {
last_write_time_ = last_write_time;
}
std::ifstream ifs(global_modules_list);
if (!ifs.good()) {
LOGE("Cannot access path %s", global_modules_list.c_str());
return false;
return modules_list;
}
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<std::string>{});
modules_list.emplace_back(std::move(module), std::unordered_set<std::string>{});
const auto &module_scope_conf = GetConfigPath(module_pkg_name + ".conf");
if (!path_exists(module_scope_conf)) {
if (!path_exists(module_scope_conf, true)) {
LOGD("module scope is not set for %s", module_pkg_name.c_str());
continue;
}
@ -204,7 +135,7 @@ namespace edxp {
LOGE("Cannot access path %s", module_scope_conf.c_str());
continue;
}
auto &scope = modules_list_.back().second;
auto &scope = modules_list.back().second;
std::string app_pkg_name;
while (std::getline(ifs_c, app_pkg_name)) {
if (!app_pkg_name.empty())
@ -218,16 +149,22 @@ namespace edxp {
return join.str();
})().c_str());
}
return true;
return modules_list;
}
bool ConfigManager::UpdateAppModuleList(const uid_t user, const std::string &pkg_name) {
UpdateConfigPath(user);
app_modules_list_.clear();
std::vector<std::string> ConfigManager::GetAppModuleList(const std::string &pkg_name) const {
std::vector<std::string> app_modules_list;
for (const auto&[module, scope]: modules_list_) {
if (scope.empty() || scope.count(pkg_name)) app_modules_list_.push_back(module);
if (scope.empty() || scope.count(pkg_name)) app_modules_list.push_back(module);
}
return !app_modules_list_.empty() || pkg_name == installer_pkg_name_;
return app_modules_list;
}
std::filesystem::file_time_type ConfigManager::GetLastWriteTime() const {
auto dynamic_path = GetConfigPath("dynamic");
if (!path_exists(dynamic_path, true))
return {};
return fs::last_write_time(dynamic_path);
}
}

View File

@ -10,6 +10,7 @@
#include <unordered_set>
#include <unordered_map>
#include "config.h"
#include "utils.h"
namespace edxp {
@ -20,19 +21,23 @@ namespace edxp {
class ConfigManager {
public:
inline static ConfigManager *GetInstance() {
if (!instance_) {
instance_ = std::make_unique<ConfigManager>();
return instances_[current_user].get();
}
inline static void SetCurrentUser(uid_t user) {
if (auto instance = instances_.find(user);
!instance->second || instance->second->NeedUpdateConfig()) {
instances_[user] = std::make_unique<ConfigManager>(user);
}
return instance_.get();
}
inline static std::unique_ptr<ConfigManager> ReleaseInstance() {
return std::move(instance_);
inline static auto ReleaseInstances() {
return std::move(instances_);
}
inline auto IsBlackWhiteListEnabled() const { return black_white_list_enabled_; }
inline auto IsDynamicModulesEnabled() const { return dynamic_modules_enabled_; }
inline auto IsBlackWhiteListEnabled() const {
return black_list_enable_ || white_list_enable_;
}
inline auto IsResourcesHookEnabled() const { return resources_hook_enabled_; }
@ -54,52 +59,50 @@ namespace edxp {
return data_path_prefix_ / installer_pkg_name_ / "conf" / suffix;
}
inline auto GetAppModulesList() const { return app_modules_list_; };
std::vector<std::string> GetAppModuleList(const std::string &pkg_name) const;
bool UpdateAppModuleList(const uid_t user, const std::string &pkg_name);
bool IsAppNeedHook(const std::string &pkg_name) const;
bool IsAppNeedHook(const uid_t user, const std::string &pkg_name);
bool NeedUpdateConfig() const {
return last_write_time_ < GetLastWriteTime();
}
bool UpdateModuleList();
private:
inline static std::unique_ptr<ConfigManager> instance_ = nullptr;
uid_t last_user_ = 0;
bool use_prot_storage_ = true;
std::filesystem::path data_path_prefix_;
std::filesystem::path installer_pkg_name_;
std::filesystem::path base_config_path_;
std::filesystem::path blacklist_path_;
std::filesystem::path whitelist_path_;
std::filesystem::path use_whitelist_path_;
bool black_white_list_enabled_ = false;
bool dynamic_modules_enabled_ = false;
bool deopt_boot_image_enabled_ = false;
bool no_module_log_enabled_ = false;
bool resources_hook_enabled_ = false;
inline static std::unordered_map<uid_t, std::unique_ptr<ConfigManager>> instances_{};
inline static uid_t current_user = 0u;
inline static bool use_prot_storage_ = GetAndroidApiLevel() >= __ANDROID_API_N__;
const uid_t user_;
const std::filesystem::path data_path_prefix_;
const std::filesystem::path installer_pkg_name_;
const bool black_list_enable_ = false;
const bool white_list_enable_ = false;
const bool deopt_boot_image_enabled_ = false;
const bool no_module_log_enabled_ = false;
const bool resources_hook_enabled_ = false;
const bool hidden_api_bypass_enabled_ = false;
// snapshot at boot
bool use_white_list_snapshot_ = false;
std::unordered_set<std::string> white_list_default_;
std::unordered_set<std::string> black_list_default_;
bool hidden_api_bypass_enabled_ = false;
const std::unordered_set<std::string> white_list_;
const std::unordered_set<std::string> black_list_;
std::vector<std::pair<std::string, std::unordered_set<std::string>>> modules_list_;
const std::vector<std::pair<std::string, std::unordered_set<std::string>>> modules_list_;
std::vector<std::string> app_modules_list_;
const std::filesystem::file_time_type last_write_time_;
std::filesystem::file_time_type last_write_time_;
ConfigManager(uid_t uid);
ConfigManager();
void UpdateConfigPath(const uid_t user);
void SnapshotBlackWhiteList();
static std::unordered_set<std::string> GetAppList(const std::filesystem::path &dir);
std::string RetrieveInstallerPkgName() const;
static std::string GetPackageNameFromBaseApkPath(const std::filesystem::path &path);
friend std::unique_ptr<ConfigManager> std::make_unique<ConfigManager>();
std::remove_const_t<decltype(modules_list_)> GetModuleList();
std::filesystem::file_time_type GetLastWriteTime() const;
friend std::unique_ptr<ConfigManager> std::make_unique<ConfigManager>(uid_t &);
};

View File

@ -231,13 +231,15 @@ namespace edxp {
jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities,
jlong effective_capabilities) {
ConfigManager::GetInstance()->UpdateModuleList(); // I don't think we need this, but anyway
ConfigManager::SetCurrentUser(0u);
app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList(
"android"); // I don't think we need this, but anyway
skip_ = false;
if (!ConfigManager::GetInstance()->IsAppNeedHook(0, "android")) {
if (!ConfigManager::GetInstance()->IsAppNeedHook("android")) {
skip_ = true;
LOGD("skip injecting xposed into android because it's whitelisted/blacklisted");
}
if (!skip_ && !ConfigManager::GetInstance()->UpdateAppModuleList(0, "android")) {
if (!skip_ && app_modules_list_.empty()) {
skip_ = true;
LOGD("skip injecting into android because no module hooks it");
}
@ -265,7 +267,7 @@ namespace edxp {
// only do work in child since FindAndCall would print log
FindAndCall(env, "forkSystemServerPost", "(I)V", res);
} else {
auto config_manager = ConfigManager::ReleaseInstance();
auto config_managers = ConfigManager::ReleaseInstances();
auto context = Context::ReleaseInstance();
LOGD("skipped android");
}
@ -298,12 +300,12 @@ namespace edxp {
return {true, uid, package_name};
}
bool Context::ShouldSkipInject(JNIEnv *env, jstring nice_name, jstring data_dir, jint uid,
jboolean is_child_zygote) {
bool Context::ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid,
bool info_res, const std::vector<std::string> &app_modules_list_,
bool is_child_zygote) {
const auto app_id = uid % PER_USER_RANGE;
const auto&[res, user, package_name] = GetAppInfoFromDir(env, data_dir);
bool skip = false;
if (!res) {
if (!info_res) {
LOGW("skip injecting into %s because it has no data dir", package_name.c_str());
skip = true;
}
@ -320,13 +322,14 @@ namespace edxp {
LOGW("skip injecting into %s because it's isolated", package_name.c_str());
}
if (!skip && !ConfigManager::GetInstance()->IsAppNeedHook(user, package_name)) {
if (!skip && !ConfigManager::GetInstance()->IsAppNeedHook(package_name)) {
skip = true;
LOGW("skip injecting xposed into %s because it's whitelisted/blacklisted",
package_name.c_str());
}
if (!skip && !ConfigManager::GetInstance()->UpdateAppModuleList(user, package_name)) {
if (!skip && app_modules_list_.empty() &&
package_name != ConfigManager::GetInstance()->GetInstallerPackageName()) {
skip = true;
LOGD("skip injecting xposed into %s because no module hooks it",
package_name.c_str());
@ -347,10 +350,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);
const JUTFString dir(env, app_data_dir, "");
const auto&[res, user, package_name] = GetAppInfoFromDir(env, app_data_dir);
ConfigManager::SetCurrentUser(user);
app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList(package_name);
skip_ = ShouldSkipInject(package_name, user, uid, res, app_modules_list_, is_child_zygote);
app_data_dir_ = app_data_dir;
nice_name_ = nice_name;
PreLoadDex(env, kInjectDexPath);
@ -369,7 +372,7 @@ namespace edxp {
res, app_data_dir_, nice_name_);
LOGD("injected xposed into %s", process_name.get());
} else {
auto config_manager = ConfigManager::ReleaseInstance();
auto config_manager = ConfigManager::ReleaseInstances();
auto context = Context::ReleaseInstance();
LOGD("skipped %s", process_name.get());
}

View File

@ -51,6 +51,8 @@ namespace edxp {
inline auto GetNiceName() const { return nice_name_; }
inline auto GetAppModulesList() const { return app_modules_list_; }
inline jclass FindClassFromLoader(JNIEnv *env, const std::string &className) const {
return FindClassFromLoader(env, className.c_str());
};
@ -93,6 +95,7 @@ namespace edxp {
jmethodID post_fixup_static_mid_ = nullptr;
bool skip_ = false;
std::vector<std::vector<signed char>> dexes;
std::vector<std::string> app_modules_list_;
Context() {}
@ -110,12 +113,15 @@ namespace edxp {
void CallPostFixupStaticTrampolinesCallback(void *class_ptr, jmethodID mid);
static bool ShouldSkipInject(JNIEnv *env, jstring nice_name, jstring data_dir, jint uid,
jboolean is_child_zygote);
static bool
ShouldSkipInject(const std::string &package_name, uid_t user, uid_t uid, bool res,
const std::vector<std::string> &app_module_list,
bool is_child_zygote);
static std::tuple<bool, uid_t, std::string> GetAppInfoFromDir(JNIEnv *env, jstring dir);
friend std::unique_ptr<Context> std::make_unique<Context>();
};
}

View File

@ -11,10 +11,6 @@ namespace edxp {
return (jboolean) ConfigManager::GetInstance()->IsBlackWhiteListEnabled();
}
static jboolean ConfigManager_isDynamicModulesEnabled(JNI_START) {
return (jboolean) ConfigManager::GetInstance()->IsDynamicModulesEnabled();
}
static jboolean ConfigManager_isResourcesHookEnabled(JNI_START) {
return (jboolean) ConfigManager::GetInstance()->IsResourcesHookEnabled();
}
@ -52,7 +48,7 @@ namespace edxp {
}
static jstring ConfigManager_getModulesList(JNI_START) {
auto module_list = ConfigManager::GetInstance()->GetAppModulesList();
auto module_list = Context::GetInstance()->GetAppModulesList();
std::ostringstream join;
std::copy(module_list.begin(), module_list.end(), std::ostream_iterator<std::string>(join, "\n"));
const auto &list = join.str();
@ -62,7 +58,6 @@ namespace edxp {
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ConfigManager, isBlackWhiteListEnabled, "()Z"),
NATIVE_METHOD(ConfigManager, isDynamicModulesEnabled, "()Z"),
NATIVE_METHOD(ConfigManager, isResourcesHookEnabled, "()Z"),
NATIVE_METHOD(ConfigManager, isDeoptBootImageEnabled, "()Z"),
NATIVE_METHOD(ConfigManager, isNoModuleLogEnabled, "()Z"),

View File

@ -12,8 +12,6 @@ public interface EdxpConfig {
String getLibSandHookName();
boolean isDynamicModulesMode();
boolean isNoModuleLogEnabled();
boolean isResourcesHookEnabled();

View File

@ -312,7 +312,7 @@ public final class XposedInit {
public static boolean loadModules(boolean callInitZygote) throws IOException {
boolean hasLoaded = !modulesLoaded.compareAndSet(false, true);
if (hasLoaded && !EdXpConfigGlobal.getConfig().isDynamicModulesMode()) {
if (hasLoaded) {
return false;
}
synchronized (moduleLoadLock) {