Move dex/jar to /data/misc
This commit is contained in:
parent
b585d722c8
commit
993ba29616
|
|
@ -29,10 +29,6 @@ inline constexpr bool is64 = Is64();
|
|||
# define LP_SELECT(lp32, lp64) (lp32)
|
||||
#endif
|
||||
|
||||
static const auto kInjectDexPath = "/system/framework/edxp.dex:"
|
||||
"/system/framework/eddalvikdx.dex:"
|
||||
"/system/framework/eddexmaker.dex"_str;
|
||||
|
||||
static const auto kEntryClassName = "com.elderdrivers.riru.edxp.core.Main"_str;
|
||||
static const auto kClassLinkerClassName = "com.elderdrivers.riru.edxp.art.ClassLinker";
|
||||
static const auto kSandHookClassName = "com.swift.sandhook.SandHook"_str;
|
||||
|
|
|
|||
|
|
@ -22,23 +22,26 @@ namespace edxp {
|
|||
return {str, size};
|
||||
}
|
||||
|
||||
inline bool path_exists(const std::filesystem::path &path, bool quite = false) {
|
||||
template<bool quite = false>
|
||||
inline bool path_exists(const std::filesystem::path &path) {
|
||||
try {
|
||||
return std::filesystem::exists(path);
|
||||
} catch (const std::filesystem::filesystem_error &e) {
|
||||
if (!quite)
|
||||
if constexpr(!quite) {
|
||||
LOGE("%s", e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void path_chown(const std::filesystem::path &path, uid_t uid, gid_t gid, bool recursively=false) {
|
||||
inline void
|
||||
path_chown(const std::filesystem::path &path, uid_t uid, gid_t gid, bool recursively = false) {
|
||||
if (chown(path.c_str(), uid, gid) != 0) {
|
||||
throw std::filesystem::filesystem_error(strerror(errno), path,
|
||||
{errno, std::system_category()});
|
||||
{errno, std::system_category()});
|
||||
}
|
||||
if(recursively) {
|
||||
for(const auto &item : std::filesystem::recursive_directory_iterator(path)) {
|
||||
if (recursively) {
|
||||
for (const auto &item : std::filesystem::recursive_directory_iterator(path)) {
|
||||
if (chown(item.path().c_str(), uid, gid) != 0) {
|
||||
throw std::filesystem::filesystem_error(strerror(errno), item.path(),
|
||||
{errno, std::system_category()});
|
||||
|
|
@ -47,7 +50,7 @@ namespace edxp {
|
|||
}
|
||||
}
|
||||
|
||||
inline std::tuple<uid_t, gid_t> path_own(const std::filesystem::path& path) {
|
||||
inline std::tuple<uid_t, gid_t> path_own(const std::filesystem::path &path) {
|
||||
struct stat sb;
|
||||
stat(path.c_str(), &sb);
|
||||
return {sb.st_uid, sb.st_gid};
|
||||
|
|
|
|||
|
|
@ -85,23 +85,16 @@ namespace edxp {
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
fs::path ConfigManager::RetrieveBaseConfigPath() const {
|
||||
fs::path misc_path("/data/adb/edxp/misc_path");
|
||||
try {
|
||||
RirudSocket rirud_socket{};
|
||||
auto path = rirud_socket.ReadFile(misc_path);
|
||||
path.erase(std::find_if(path.rbegin(), path.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), path.end());
|
||||
return fs::path("/data/misc") / path / std::to_string(user_);
|
||||
} catch (const RirudSocket::RirudSocketException &e) {
|
||||
LOGE("%s", e.what());
|
||||
if (auto misc_path = GetMiscPath(); !misc_path.empty()) {
|
||||
return misc_path / std::to_string(user_);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::string ConfigManager::RetrieveInstallerPkgName() const {
|
||||
std::string installer_pkg_name_path = GetConfigPath("installer");
|
||||
if (!path_exists(installer_pkg_name_path, true)) {
|
||||
if (!path_exists<true>(installer_pkg_name_path)) {
|
||||
LOGW("installer not set, using default one %s", kPrimaryInstallerPkgName.c_str());
|
||||
return kPrimaryInstallerPkgName;
|
||||
}
|
||||
|
|
@ -217,7 +210,7 @@ namespace edxp {
|
|||
auto &[module_path, scope] = modules_list[module_pkg_name];
|
||||
module_path.assign(std::move(module));
|
||||
const auto &module_scope_conf = GetConfigPath(module_pkg_name + ".conf");
|
||||
if (!path_exists(module_scope_conf, true)) {
|
||||
if (!path_exists<true>(module_scope_conf)) {
|
||||
LOGD("module scope is not set for %s", module_pkg_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
|
@ -253,7 +246,7 @@ namespace edxp {
|
|||
|
||||
std::filesystem::file_time_type ConfigManager::GetLastWriteTime() const {
|
||||
auto modules_list = GetConfigPath("modules.list");
|
||||
if (!path_exists(modules_list, true))
|
||||
if (!path_exists<true>(modules_list))
|
||||
return {};
|
||||
return fs::last_write_time(modules_list);
|
||||
}
|
||||
|
|
@ -262,7 +255,7 @@ namespace edxp {
|
|||
if (base_config_path_.empty()) return false;
|
||||
try {
|
||||
fs::create_directories(base_config_path_);
|
||||
fs::permissions(base_config_path_.parent_path(),
|
||||
fs::permissions(GetMiscPath(),
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_exec);
|
||||
fs::permissions(base_config_path_,
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_exec);
|
||||
|
|
@ -286,7 +279,7 @@ namespace edxp {
|
|||
try {
|
||||
if (modules_list_.count(pkg_name)) {
|
||||
auto prefs_path = GetPrefsPath(pkg_name);
|
||||
if (!path_exists(prefs_path, true)) {
|
||||
if (!path_exists<true>(prefs_path)) {
|
||||
fs::create_directories(prefs_path);
|
||||
} else {
|
||||
const auto &[r_uid, r_gid] = path_own(prefs_path);
|
||||
|
|
@ -301,11 +294,11 @@ namespace edxp {
|
|||
}
|
||||
if (pkg_name == installer_pkg_name_) {
|
||||
auto conf_path = GetConfigPath();
|
||||
if (!path_exists(conf_path, true)) {
|
||||
if (!path_exists<true>(conf_path)) {
|
||||
fs::create_directories(conf_path);
|
||||
}
|
||||
auto log_path = GetLogPath();
|
||||
if (!path_exists(log_path, true)) {
|
||||
if (!path_exists<true>(log_path)) {
|
||||
fs::create_directories(log_path);
|
||||
}
|
||||
fs::permissions(conf_path, fs::perms::owner_all | fs::perms::group_all);
|
||||
|
|
@ -322,4 +315,32 @@ namespace edxp {
|
|||
}
|
||||
}
|
||||
|
||||
auto ConfigManager::GetMiscPath() -> decltype(misc_path_) {
|
||||
if (misc_path_.empty()) {
|
||||
fs::path misc_path("/data/adb/edxp/misc_path");
|
||||
try {
|
||||
RirudSocket rirud_socket{};
|
||||
auto path = rirud_socket.ReadFile(misc_path);
|
||||
path.erase(std::find_if(path.rbegin(), path.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), path.end());
|
||||
misc_path_ = fs::path("/data/misc") / path;
|
||||
} catch (const RirudSocket::RirudSocketException &e) {
|
||||
LOGE("%s", e.what());
|
||||
}
|
||||
}
|
||||
return misc_path_;
|
||||
}
|
||||
|
||||
auto ConfigManager::GetInjectDexPaths() -> decltype(inject_dex_paths_) {
|
||||
if (inject_dex_paths_.empty()) {
|
||||
std::transform(kXposedInjectDexPath.begin(), kXposedInjectDexPath.end(),
|
||||
std::back_inserter(inject_dex_paths_),
|
||||
[](auto i) {
|
||||
return GetFrameworkPath(i);
|
||||
});
|
||||
}
|
||||
return inject_dex_paths_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,10 +14,17 @@
|
|||
|
||||
namespace edxp {
|
||||
|
||||
static const std::string kPrimaryInstallerPkgName = "org.meowcat.edxposed.manager";
|
||||
static const std::string kXposedPropPath = "/system/framework/edconfig.jar";
|
||||
|
||||
class ConfigManager {
|
||||
private:
|
||||
inline static const auto kPrimaryInstallerPkgName = "org.meowcat.edxposed.manager"_str;
|
||||
inline static const auto kXposedPropName = "edconfig.jar"_str;
|
||||
inline static const std::vector<std::string> kXposedInjectDexPath = {
|
||||
"edxp.dex",
|
||||
"eddalvikdx.dex",
|
||||
"eddexmaker.dex",
|
||||
};
|
||||
|
||||
public:
|
||||
inline static ConfigManager *GetInstance() {
|
||||
return instances_[current_user].get();
|
||||
|
|
@ -25,7 +32,8 @@ namespace edxp {
|
|||
|
||||
inline static void SetCurrentUser(uid_t user) {
|
||||
if (auto instance = instances_.find(user);
|
||||
instance == instances_.end() || !instance->second || instance->second->NeedUpdateConfig()) {
|
||||
instance == instances_.end() || !instance->second ||
|
||||
instance->second->NeedUpdateConfig()) {
|
||||
instances_[user] = std::make_unique<ConfigManager>(user);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +42,8 @@ namespace edxp {
|
|||
return std::move(instances_);
|
||||
}
|
||||
|
||||
inline auto IsInitialized() const { return initialized_; }
|
||||
|
||||
// Always true now
|
||||
inline auto IsBlackWhiteListEnabled() const { return true; }
|
||||
|
||||
|
|
@ -45,12 +55,16 @@ namespace edxp {
|
|||
|
||||
inline auto GetInstallerPackageName() const { return installer_pkg_name_; }
|
||||
|
||||
inline auto GetXposedPropPath() const { return kXposedPropPath; }
|
||||
|
||||
inline auto GetLibSandHookName() const { return kLibSandHookName; }
|
||||
|
||||
inline auto GetDataPathPrefix() const { return data_path_prefix_; }
|
||||
|
||||
inline static auto GetFrameworkPath(const std::string &suffix = {}) {
|
||||
return GetMiscPath() / "framework" / suffix;
|
||||
}
|
||||
|
||||
inline auto GetXposedPropPath() const { return GetFrameworkPath(kXposedPropName); }
|
||||
|
||||
inline auto GetConfigPath(const std::string &suffix = {}) const {
|
||||
return base_config_path_ / "conf" / suffix;
|
||||
}
|
||||
|
|
@ -75,11 +89,16 @@ namespace edxp {
|
|||
|
||||
void EnsurePermission(const std::string &pkg_name, uid_t uid) const;
|
||||
|
||||
|
||||
private:
|
||||
inline static std::unordered_map<uid_t, std::unique_ptr<ConfigManager>> instances_{};
|
||||
inline static uid_t current_user = 0u;
|
||||
inline static std::filesystem::path misc_path_;
|
||||
inline static std::vector<std::filesystem::path> inject_dex_paths_;
|
||||
inline static const bool use_prot_storage_ = GetAndroidApiLevel() >= __ANDROID_API_N__;
|
||||
|
||||
static decltype(misc_path_) GetMiscPath();
|
||||
|
||||
const uid_t user_;
|
||||
const std::filesystem::path data_path_prefix_;
|
||||
const std::filesystem::path base_config_path_;
|
||||
|
|
@ -114,6 +133,9 @@ namespace edxp {
|
|||
friend std::unique_ptr<ConfigManager> std::make_unique<ConfigManager>(uid_t &);
|
||||
|
||||
std::filesystem::path RetrieveBaseConfigPath() const;
|
||||
|
||||
public:
|
||||
static decltype(inject_dex_paths_) GetInjectDexPaths();
|
||||
};
|
||||
|
||||
} // namespace edxp
|
||||
|
|
|
|||
|
|
@ -58,17 +58,8 @@ namespace edxp {
|
|||
CallPostFixupStaticTrampolinesCallback(class_ptr, post_fixup_static_mid_);
|
||||
}
|
||||
|
||||
void Context::PreLoadDex(JNIEnv *env, const std::string &dex_path) {
|
||||
if (LIKELY(!dexes.empty())) return;
|
||||
std::vector<std::string> paths;
|
||||
{
|
||||
std::istringstream is(dex_path);
|
||||
std::string path;
|
||||
while (std::getline(is, path, ':')) {
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
for (const auto &path: paths) {
|
||||
void Context::PreLoadDex(const std::vector<fs::path> &dex_paths) {
|
||||
for (const auto &path: dex_paths) {
|
||||
std::ifstream is(path, std::ios::binary);
|
||||
if (!is.good()) {
|
||||
LOGE("Cannot load path %s", path.c_str());
|
||||
|
|
@ -234,15 +225,20 @@ namespace edxp {
|
|||
app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList(
|
||||
"android"); // I don't think we need this, but anyway
|
||||
skip_ = false;
|
||||
if (!ConfigManager::GetInstance()->IsAppNeedHook("android")) {
|
||||
if (!ConfigManager::GetInstance()->IsInitialized()) {
|
||||
LOGE("skip injecting into android because configurations are not loaded properly");
|
||||
}
|
||||
if (skip_ && !ConfigManager::GetInstance()->IsAppNeedHook("android")) {
|
||||
skip_ = true;
|
||||
LOGW("skip injecting xposed into android because it's whitelisted/blacklisted");
|
||||
LOGW("skip injecting into android because it's whitelisted/blacklisted");
|
||||
}
|
||||
if (!skip_ && app_modules_list_.empty()) {
|
||||
skip_ = true;
|
||||
LOGW("skip injecting into android because no module hooks it");
|
||||
}
|
||||
PreLoadDex(env, kInjectDexPath);
|
||||
if(!skip_) {
|
||||
PreLoadDex(ConfigManager::GetInjectDexPaths());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -278,10 +274,11 @@ namespace edxp {
|
|||
}
|
||||
|
||||
std::tuple<bool, uid_t, std::string>
|
||||
Context::GetAppInfoFromDir(JNIEnv *env, jstring dir) {
|
||||
Context::GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name) {
|
||||
uid_t uid = 0;
|
||||
JUTFString app_data_dir(env, dir);
|
||||
if (!app_data_dir) return {false, 0, {}};
|
||||
JUTFString name(env, nice_name);
|
||||
if (!app_data_dir) return {false, 0, name.get()};
|
||||
fs::path path(app_data_dir.get());
|
||||
std::vector<std::string> splits(path.begin(), path.end());
|
||||
if (splits.size() < 5u) {
|
||||
|
|
@ -304,7 +301,11 @@ namespace edxp {
|
|||
bool is_child_zygote) {
|
||||
const auto app_id = uid % PER_USER_RANGE;
|
||||
bool skip = false;
|
||||
if (!info_res) {
|
||||
if (!ConfigManager::GetInstance()->IsInitialized()) {
|
||||
LOGE("skip injecting into %s because configurations are not loaded properly", package_name.c_str());
|
||||
skip = true;
|
||||
}
|
||||
if (!skip && !info_res) {
|
||||
LOGW("skip injecting into %s because it has no data dir", package_name.c_str());
|
||||
skip = true;
|
||||
}
|
||||
|
|
@ -349,14 +350,16 @@ namespace edxp {
|
|||
jboolean is_child_zygote,
|
||||
jstring instruction_set,
|
||||
jstring 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);
|
||||
ConfigManager::GetInstance()->EnsurePermission(package_name, uid % PER_USER_RANGE);
|
||||
const auto&[res, user, package_name] = GetAppInfoFromDir(env, app_data_dir, nice_name);
|
||||
app_data_dir_ = app_data_dir;
|
||||
nice_name_ = nice_name;
|
||||
PreLoadDex(env, kInjectDexPath);
|
||||
ConfigManager::SetCurrentUser(user);
|
||||
skip_ = ShouldSkipInject(package_name, user, uid, res, app_modules_list_, is_child_zygote);
|
||||
if(!skip_) {
|
||||
app_modules_list_ = ConfigManager::GetInstance()->GetAppModuleList(package_name);
|
||||
ConfigManager::GetInstance()->EnsurePermission(package_name, uid % PER_USER_RANGE);
|
||||
PreLoadDex(ConfigManager::GetInjectDexPaths());
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include "utils.h"
|
||||
|
||||
namespace edxp {
|
||||
static const auto SYSTEM_SERVER_DATA_DIR = "/data/user/0/android"_str;
|
||||
enum Variant {
|
||||
NONE = 0,
|
||||
YAHFA = 1,
|
||||
|
|
@ -99,7 +98,7 @@ namespace edxp {
|
|||
|
||||
Context() {}
|
||||
|
||||
void PreLoadDex(JNIEnv *env, const std::string &dex_path);
|
||||
void PreLoadDex(const std::vector<std::filesystem::path> &dex_path);
|
||||
|
||||
void InjectDexAndInit(JNIEnv *env);
|
||||
|
||||
|
|
@ -118,7 +117,7 @@ namespace edxp {
|
|||
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);
|
||||
static std::tuple<bool, uid_t, std::string> GetAppInfoFromDir(JNIEnv *env, jstring dir, jstring nice_name);
|
||||
|
||||
friend std::unique_ptr<Context> std::make_unique<Context>();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,18 +36,6 @@ PROP_PRODUCT=$(getprop ro.build.product)
|
|||
PROP_BRAND=$(getprop ro.product.brand)
|
||||
PROP_MANUFACTURER=$(getprop ro.product.manufacturer)
|
||||
|
||||
JAR_EDXP="$(getRandomNameExist 8 "" ".dex" "
|
||||
/system/framework
|
||||
").dex"
|
||||
JAR_EDDALVIKDX="$(getRandomNameExist 8 "" ".dex" "
|
||||
/system/framework
|
||||
").dex"
|
||||
JAR_EDDEXMAKER="$(getRandomNameExist 8 "" ".dex" "
|
||||
/system/framework
|
||||
").dex"
|
||||
#JAR_EDCONFIG="$(getRandomNameExist 8 "" ".jar" "
|
||||
#/system/framework
|
||||
#").jar"
|
||||
LIB_RIRU_EDXP="libriru_${RIRU_EDXP}.so"
|
||||
LIB_SANDHOOK_EDXP="lib$(getRandomNameExist 13 "lib" ".so" "
|
||||
/system/lib
|
||||
|
|
@ -287,14 +275,30 @@ if [[ "${OLD_MAGISK}" == true ]]; then
|
|||
rm "${MODPATH}"/sepolicy.rule
|
||||
fi
|
||||
|
||||
ui_print "- Creating configuration directories"
|
||||
if [[ -f /data/adb/edxp/misc_path ]]; then
|
||||
MISC_PATH=$(cat /data/adb/edxp/misc_path)
|
||||
ui_print "- Use previous path $MISC_PATH"
|
||||
else
|
||||
MISC_PATH="edxp_$(tr -cd 'A-Za-z0-9' < /dev/urandom | head -c16)"
|
||||
ui_print "- Use new path $MISC_PATH"
|
||||
mkdir -p /data/adb/edxp || abort "! Can't create adb path"
|
||||
echo "$MISC_PATH" > /data/adb/edxp/misc_path || abort "! Can't store configuration path"
|
||||
fi
|
||||
set_perm_recursive /data/adb/edxp root root 0700 0600 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||
mkdir -p /data/misc/$MISC_PATH || abort "! Can't create configuration path"
|
||||
set_perm /data/misc/$MISC_PATH root root 0771 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||
echo "rm -rf /data/misc/$MISC_PATH" >> "$MODPATH/uninstall.sh" || abort "! Can't write uninstall.sh"
|
||||
echo "rm -rf /data/adb/edxp" >> "$MODPATH/uninstall.sh" || abort "! Can't write uninstall.sh"
|
||||
|
||||
|
||||
ui_print "- Copying framework libraries"
|
||||
|
||||
mv "${MODPATH}/system/framework/eddalvikdx.dex" "${MODPATH}/system/framework/${JAR_EDDALVIKDX}"
|
||||
mv "${MODPATH}/system/framework/edxp.dex" "${MODPATH}/system/framework/${JAR_EDXP}"
|
||||
mv "${MODPATH}/system/framework/eddexmaker.dex" "${MODPATH}/system/framework/${JAR_EDDEXMAKER}"
|
||||
#mv "${MODPATH}/system/framework/edconfig.jar" "${MODPATH}/system/framework/${JAR_EDCONFIG}"
|
||||
mv "${MODPATH}/system/lib/libriru_edxp.so" "${MODPATH}/system/lib/${LIB_RIRU_EDXP}"
|
||||
rm -rf "/data/misc/$MISC_PATH/framework"
|
||||
mv "${MODPATH}/system/framework" "/data/misc/$MISC_PATH/framework"
|
||||
set_perm_recursive /data/misc/$MISC_PATH/framework root root 0755 0644 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||
|
||||
mv "${MODPATH}/system/lib/libriru_edxp.so" "${MODPATH}/system/lib/${LIB_RIRU_EDXP}"
|
||||
if [[ "${IS64BIT}" == true ]]; then
|
||||
mv "${MODPATH}/system/lib64/libriru_edxp.so" "${MODPATH}/system/lib64/${LIB_RIRU_EDXP}"
|
||||
fi
|
||||
|
|
@ -352,23 +356,6 @@ rm "${RIRU_TARGET}/module.prop"
|
|||
cp "${MODPATH}/module.prop" "${RIRU_TARGET}/module.prop" || abort "! Can't create ${RIRU_TARGET}/module.prop"
|
||||
|
||||
set_perm_recursive "${MODPATH}" 0 0 0755 0644
|
||||
|
||||
ui_print "- Creating configuration directories"
|
||||
if [[ -f /data/adb/edxp/misc_path ]]; then
|
||||
MISC_PATH=$(cat /data/adb/edxp/misc_path)
|
||||
ui_print "- Use previous path $MISC_PATH"
|
||||
else
|
||||
MISC_PATH="edxp_$(tr -cd 'A-Za-z0-9' < /dev/urandom | head -c16)"
|
||||
ui_print "- Use new path $MISC_PATH"
|
||||
mkdir -p /data/adb/edxp || abort "! Can't create adb path"
|
||||
echo "$MISC_PATH" > /data/adb/edxp/misc_path || abort "! Can't store configuration path"
|
||||
fi
|
||||
set_perm_recursive /data/adb/edxp root root 0700 0600 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||
mkdir -p /data/misc/$MISC_PATH || abort "! Can't create configuration path"
|
||||
set_perm /data/misc/$MISC_PATH root root 0771 "u:object_r:magisk_file:s0" || abort "! Can't set permission"
|
||||
echo "rm -rf /data/misc/$MISC_PATH" >> "$MODPATH/uninstall.sh" || abort "! Can't write uninstall.sh"
|
||||
echo "rm -rf /data/adb/edxp" >> "$MODPATH/uninstall.sh" || abort "! Can't write uninstall.sh"
|
||||
|
||||
ui_print "- Welcome to EdXposed ${VERSION}!"
|
||||
|
||||
# before Magisk 16e4c67, sepolicy.rule is copied on the second reboot
|
||||
|
|
|
|||
Loading…
Reference in New Issue