Skip to content

Commit

Permalink
Module scope
Browse files Browse the repository at this point in the history
  • Loading branch information
LoveSy authored and kotori2 committed Nov 24, 2020
1 parent 14b8536 commit 2d7b982
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,5 @@ private static boolean isFileExists(String path) {

public static native String getDataPathPrefix();

public static native boolean isAppNeedHook(String appDataDir);

public static native String getModulesList();
}
98 changes: 62 additions & 36 deletions edxp-core/src/main/cpp/main/src/config_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <logging.h>
#include <climits>
#include <fstream>
#include <sstream>
#include "art/runtime/native/native_util.h"
#include "config_manager.h"

Expand Down Expand Up @@ -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_);
Expand All @@ -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_);
Expand All @@ -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, {}};
}
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, {}};
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);
}
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;
Expand All @@ -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
Expand All @@ -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;
}
}
Expand All @@ -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());
Expand All @@ -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();
}

}
21 changes: 13 additions & 8 deletions edxp-core/src/main/cpp/main/src/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <art/runtime/native/native_util.h>
#include <filesystem>
#include <unordered_set>
#include <unordered_map>

namespace edxp {

Expand All @@ -17,7 +18,6 @@ namespace edxp {

class ConfigManager {
public:

inline static ConfigManager *GetInstance() {
if (!instance_) {
instance_ = std::make_unique<ConfigManager>();
Expand Down Expand Up @@ -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 UpdateModuleList();
bool IsAppNeedHook(const uid_t user, const std::string &pkg_name);

static std::tuple<bool, uid_t, std::string>
GetAppInfoFromDir(const std::string &app_data_dir);
bool UpdateModuleList();

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_;
Expand All @@ -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_;

Expand All @@ -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
Expand Down
81 changes: 65 additions & 16 deletions edxp-core/src/main/cpp/main/src/edxp_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -226,46 +228,92 @@ 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
}
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;
}
if (!skip && is_child_zygote) {
skip = true;
LOGW("skip injecting into %s because it's a child zygote", package_name.get());
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;
}
Expand All @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion edxp-core/src/main/cpp/main/src/edxp_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <unistd.h>
#include <vector>
#include <string>
#include <tuple>
#include <string_view>
#include "utils.h"

Expand Down Expand Up @@ -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>();
};

}
Loading

0 comments on commit 2d7b982

Please sign in to comment.