Module scope
This commit is contained in:
parent
14b85366a1
commit
2d7b982ff5
|
|
@ -49,7 +49,5 @@ public class ConfigManager {
|
|||
|
||||
public static native String getDataPathPrefix();
|
||||
|
||||
public static native boolean isAppNeedHook(String appDataDir);
|
||||
|
||||
public static native String getModulesList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <logging.h>
|
||||
#include <climits>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#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<bool, uid_t, std::string>
|
||||
ConfigManager::GetAppInfoFromDir(const std::string &app_data_dir) {
|
||||
uid_t uid = 0;
|
||||
fs::path path(app_data_dir);
|
||||
std::vector<std::string> 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<std::string> 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::string>(std::istreambuf_iterator<char>(ifs),
|
||||
std::istreambuf_iterator<char>());
|
||||
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>{});
|
||||
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<std::string>(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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <art/runtime/native/native_util.h>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace edxp {
|
||||
|
||||
|
|
@ -17,7 +18,6 @@ namespace edxp {
|
|||
|
||||
class ConfigManager {
|
||||
public:
|
||||
|
||||
inline static ConfigManager *GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = std::make_unique<ConfigManager>();
|
||||
|
|
@ -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<bool, uid_t, std::string>
|
||||
GetAppInfoFromDir(const std::string &app_data_dir);
|
||||
|
||||
private:
|
||||
inline static std::unique_ptr<ConfigManager> 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<std::string> black_list_default_;
|
||||
bool hidden_api_bypass_enabled_ = false;
|
||||
|
||||
std::unique_ptr<std::string> modules_list_ = nullptr;
|
||||
std::vector<std::pair<std::string, std::unordered_set<std::string>>> modules_list_;
|
||||
|
||||
std::vector<std::string> 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<ConfigManager> std::make_unique<ConfigManager>();
|
||||
|
||||
};
|
||||
|
||||
} // namespace edxp
|
||||
|
|
|
|||
|
|
@ -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<bool, uid_t, std::string>
|
||||
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<std::string> 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);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <string_view>
|
||||
#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<Context> std::make_unique<Context>();
|
||||
static std::tuple<bool, uid_t, std::string> GetAppInfoFromDir(JNIEnv *env, jstring dir);
|
||||
|
||||
friend std::unique_ptr<Context> std::make_unique<Context>();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <config_manager.h>
|
||||
#include <nativehelper/jni_macros.h>
|
||||
#include <native_util.h>
|
||||
#include <sstream>
|
||||
#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<std::string>(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;"),
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue