diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/AppMigration.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/AppMigration.kt index 3c977430a..cac9555fe 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/AppMigration.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/tasks/AppMigration.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.os.Build import android.widget.Toast import com.topjohnwu.magisk.StubApk +import com.topjohnwu.magisk.core.AppApkPath import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Const @@ -193,7 +194,7 @@ object AppMigration { } Config.suManager = pkg - val cmd = "adb_pm_install $repack $pkg" + val cmd = "touch $AppApkPath; adb_pm_install $repack $pkg" if (Shell.cmd(cmd).exec().isSuccess) return true try { @@ -242,7 +243,7 @@ object AppMigration { dialog.dismiss() } Config.suManager = "" - val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME" + val cmd = "touch $AppApkPath; adb_pm_install $apk $APP_PACKAGE_NAME" if (Shell.cmd(cmd).await().isSuccess) return val success = withContext(Dispatchers.IO) { try { diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index 9c0126610..8cbbef59f 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -204,7 +204,6 @@ void MagiskD::boot_complete() const { xmkdir(SECURE_DIR, 0700); // Ensure manager exists - check_pkg_refresh(); get_manager(0, nullptr, true); reset_zygisk(true); diff --git a/native/src/core/include/core.hpp b/native/src/core/include/core.hpp index 09005df06..a287f521a 100644 --- a/native/src/core/include/core.hpp +++ b/native/src/core/include/core.hpp @@ -62,11 +62,8 @@ void zygisk_handler(int client, const sock_cred *cred); // Package void preserve_stub_apk(); -void check_pkg_refresh(); std::vector get_app_no_list(); -// Call check_pkg_refresh() before calling get_manager(...) -// to make sure the package state is invalidated! -int get_manager(int user_id = 0, std::string *pkg = nullptr, bool install = false); +int get_manager(int user, std::string *pkg = nullptr, bool install = false); void prune_su_access(); // Module stuffs diff --git a/native/src/core/package.cpp b/native/src/core/package.cpp index 8aba61264..eeaac3099 100644 --- a/native/src/core/package.cpp +++ b/native/src/core/package.cpp @@ -15,31 +15,22 @@ using rust::Vec; static pthread_mutex_t pkg_lock = PTHREAD_MUTEX_INITIALIZER; // pkg_lock protects all following variables -static int mgr_app_id = -1; -static bool skip_mgr_check; -static timespec *app_ts; -static string *mgr_pkg; -static Vec *mgr_cert; static int stub_apk_fd = -1; -static const Vec *default_cert; +static int repackaged_app_id = -1; // Only used by dyn +static string repackaged_pkg; +static Vec repackaged_cert; +static Vec trusted_cert; +static map> user_to_check; +enum status { + INSTALLED, + NO_INSTALLED, + CERT_MISMATCH, +}; static bool operator==(const Vec &a, const Vec &b) { return a.size() == b.size() && memcmp(a.data(), b.data(), a.size()) == 0; } -void check_pkg_refresh() { - mutex_guard g(pkg_lock); - if (app_ts == nullptr) - default_new(app_ts); - if (struct stat st{}; stat("/data/app", &st) == 0) { - if (memcmp(app_ts, &st.st_mtim, sizeof(timespec)) == 0) { - return; - } - memcpy(app_ts, &st.st_mtim, sizeof(timespec)); - } - skip_mgr_check = false; -} - // app_id = app_no + AID_APP_START // app_no range: [0, 9999] vector get_app_no_list() { @@ -77,9 +68,7 @@ void preserve_stub_apk() { string stub_path = get_magisk_tmp() + "/stub.apk"s; stub_apk_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC); unlink(stub_path.data()); - auto cert = read_certificate(stub_apk_fd, -1); - if (!cert.empty()) - default_cert = new Vec(std::move(cert)); + trusted_cert = read_certificate(stub_apk_fd, MAGISK_VER_CODE); lseek(stub_apk_fd, 0, SEEK_SET); } @@ -96,194 +85,174 @@ static void install_stub() { install_apk(apk); } -int get_manager(int user_id, string *pkg, bool install) { +static status check_dyn(int user, string &pkg) { + struct stat st{}; + char apk[PATH_MAX]; + ssprintf(apk, sizeof(apk), + "%s/%d/%s/dyn/current.apk", APP_DATA_DIR, user, pkg.data()); + int dyn = open(apk, O_RDONLY | O_CLOEXEC); + if (dyn < 0) { + LOGW("pkg: no dyn APK, ignore\n"); + return NO_INSTALLED; + } + auto cert = read_certificate(dyn, MAGISK_VER_CODE); + fstat(dyn, &st); + close(dyn); + + if (cert.empty() || cert != trusted_cert) { + LOGE("pkg: dyn APK signature mismatch: %s\n", apk); +#if ENFORCE_SIGNATURE + clear_pkg(pkg.data(), user); + return CERT_MISMATCH; +#endif + } + + repackaged_app_id = to_app_id(st.st_uid); + user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec); + return INSTALLED; +} + +static status check_stub(int user, string &pkg) { + struct stat st{}; + byte_array buf; + find_apk_path(pkg, buf); + string apk((const char *) buf.buf(), buf.sz()); + int fd = xopen(apk.data(), O_RDONLY | O_CLOEXEC); + if (fd < 0) + return NO_INSTALLED; + auto cert = read_certificate(fd, -1); + fstat(fd, &st); + close(fd); + + if (cert.empty() || (pkg == repackaged_pkg && cert != repackaged_cert)) { + LOGE("pkg: repackaged APK signature invalid: %s\n", apk.data()); + uninstall_pkg(pkg.data()); + return CERT_MISMATCH; + } + + repackaged_pkg.swap(pkg); + repackaged_cert.swap(cert); + user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec); + + return INSTALLED; +} + +static status check_orig(int user) { + struct stat st{}; + byte_array buf; + find_apk_path(JAVA_PACKAGE_NAME, buf); + string apk((const char *) buf.buf(), buf.sz()); + int fd = xopen(apk.data(), O_RDONLY | O_CLOEXEC); + if (fd < 0) + return NO_INSTALLED; + auto cert = read_certificate(fd, MAGISK_VER_CODE); + fstat(fd, &st); + close(fd); + + if (cert.empty() || cert != trusted_cert) { + LOGE("pkg: APK signature mismatch: %s\n", apk.data()); +#if ENFORCE_SIGNATURE + uninstall_pkg(JAVA_PACKAGE_NAME); + return CERT_MISMATCH; +#endif + } + + user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec); + return INSTALLED; +} + +static int get_pkg_uid(int user, const char *pkg) { + char path[PATH_MAX]; + struct stat st{}; + ssprintf(path, sizeof(path), "%s/%d/%s", APP_DATA_DIR, user, pkg); + if (stat(path, &st) == 0) { + return st.st_uid; + } + return -1; +} + +int get_manager(int user, string *pkg, bool install) { mutex_guard g(pkg_lock); - char app_path[128]; struct stat st{}; - if (mgr_pkg == nullptr) - default_new(mgr_pkg); - if (mgr_cert == nullptr) - default_new(mgr_cert); - - auto check_dyn = [&](int u) -> bool { -#if ENFORCE_SIGNATURE - ssprintf(app_path, sizeof(app_path), - "%s/%d/%s/dyn/current.apk", APP_DATA_DIR, u, mgr_pkg->data()); - int dyn = open(app_path, O_RDONLY | O_CLOEXEC); - if (dyn < 0) { - LOGW("pkg: no dyn APK, ignore\n"); - return false; + const auto &[path, time] = user_to_check[user]; + if (stat(path.data(), &st) == 0 && st.st_ctim.tv_sec == time) { + // no APK + if (path == "/data/system/packages.xml") { + if (install) install_stub(); + if (pkg) pkg->clear(); + return -1; } - auto cert = read_certificate(dyn, MAGISK_VER_CODE); - bool mismatch = default_cert && cert != *default_cert; - close(dyn); - if (mismatch) { - LOGE("pkg: dyn APK signature mismatch: %s\n", app_path); - clear_pkg(mgr_pkg->data(), u); - return false; + // dyn APK is still the same + if (path.starts_with(APP_DATA_DIR)) { + if (pkg) *pkg = repackaged_pkg; + return user * AID_USER_OFFSET + repackaged_app_id; } -#endif - return true; - }; - - if (skip_mgr_check) { - if (mgr_app_id >= 0) { - // Just need to check whether the app is installed in the user - const char *name = mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data(); - ssprintf(app_path, sizeof(app_path), "%s/%d/%s", APP_DATA_DIR, user_id, name); - if (access(app_path, F_OK) == 0) { - // Always check dyn signature for repackaged app - if (!mgr_pkg->empty() && !check_dyn(user_id)) - goto ignore; - if (pkg) *pkg = name; - return user_id * AID_USER_OFFSET + mgr_app_id; + // stub APK is still the same + if (!repackaged_pkg.empty()) { + if (check_dyn(user, repackaged_pkg) == INSTALLED) { + if (pkg) *pkg = repackaged_pkg; + return user * AID_USER_OFFSET + repackaged_app_id; } else { - goto not_found; + if (pkg) pkg->clear(); + return -1; } } - } else { - // Here, we want to actually find the manager app and cache the results. - // This means that we check all users, not just the requested user. - // Certificates are also verified to prevent manipulation. - - skip_mgr_check = true; - - db_strings str; - get_db_strings(str, SU_MANAGER); - - vector users; - bool collected = false; - - auto collect_users = [&] { - if (collected) - return; - collected = true; - auto data_dir = xopen_dir(APP_DATA_DIR); - if (!data_dir) - return; - dirent *entry; - while ((entry = xreaddir(data_dir.get()))) { - // Only collect users not requested as we've already checked it - if (int u = parse_int(entry->d_name); u >= 0 && u != user_id) - users.push_back(parse_int(entry->d_name)); - } - }; - - if (!str[SU_MANAGER].empty()) { - // Check the repackaged package name - - bool invalid = false; - auto check_stub_apk = [&](int u) -> bool { - ssprintf(app_path, sizeof(app_path), - "%s/%d/%s", APP_DATA_DIR, u, str[SU_MANAGER].data()); - if (stat(app_path, &st) == 0) { - int app_id = to_app_id(st.st_uid); - - byte_array apk; - find_apk_path(str[SU_MANAGER], apk); - int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC); - auto cert = read_certificate(fd, -1); - close(fd); - - // Verify validity - if (str[SU_MANAGER] == *mgr_pkg) { - if (app_id != mgr_app_id || cert.empty() || cert != *mgr_cert) { - // app ID or cert should never change - LOGE("pkg: repackaged APK signature invalid: %s\n", apk.buf()); - uninstall_pkg(mgr_pkg->data()); - invalid = true; - install = true; - return false; - } - } - - mgr_pkg->swap(str[SU_MANAGER]); - mgr_app_id = app_id; - mgr_cert->swap(cert); - return true; - } - return false; - }; - - if (check_stub_apk(user_id)) { - if (!check_dyn(user_id)) - goto ignore; - if (pkg) *pkg = *mgr_pkg; - return st.st_uid; - } - if (!invalid) { - collect_users(); - for (int u : users) { - if (check_stub_apk(u)) { - // Found repackaged app, but not installed in the requested user - goto not_found; - } - if (invalid) - break; - } - } - - // Repackaged app not found, fall through - } - - // Check the original package name - - bool invalid = false; - auto check_apk = [&](int u) -> bool { - ssprintf(app_path, sizeof(app_path), "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, u); - if (stat(app_path, &st) == 0) { -#if ENFORCE_SIGNATURE - byte_array apk; - find_apk_path(JAVA_PACKAGE_NAME, apk); - int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC); - auto cert = read_certificate(fd, MAGISK_VER_CODE); - close(fd); - if (default_cert && cert != *default_cert) { - // Found APK with invalid signature, force replace with stub - LOGE("pkg: APK signature mismatch: %s\n", apk.buf()); - uninstall_pkg(JAVA_PACKAGE_NAME); - invalid = true; - install = true; - return false; - } -#endif - mgr_pkg->clear(); - mgr_cert->clear(); - mgr_app_id = to_app_id(st.st_uid); - return true; - } - return false; - }; - - if (check_apk(user_id)) { + // orig APK is still the same + int uid = get_pkg_uid(user, JAVA_PACKAGE_NAME); + if (uid < 0) { + if (pkg) pkg->clear(); + return -1; + } else { if (pkg) *pkg = JAVA_PACKAGE_NAME; - return st.st_uid; - } - if (!invalid) { - collect_users(); - for (int u : users) { - if (check_apk(u)) { - // Found app, but not installed in the requested user - goto not_found; - } - if (invalid) - break; - } + return uid; } } - // No manager app is found, clear all cached value - mgr_app_id = -1; - mgr_pkg->clear(); - mgr_cert->clear(); - if (install) - install_stub(); + db_strings str; + get_db_strings(str, SU_MANAGER); -not_found: - LOGW("pkg: cannot find %s for user=[%d]\n", - mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data(), user_id); -ignore: + if (!str[SU_MANAGER].empty()) { + switch (check_stub(user, str[SU_MANAGER])) { + case INSTALLED: + if (check_dyn(user, repackaged_pkg) == INSTALLED) { + if (pkg) *pkg = repackaged_pkg; + return user * AID_USER_OFFSET + repackaged_app_id; + } else { + if (pkg) pkg->clear(); + return -1; + } + case CERT_MISMATCH: + install = true; + case NO_INSTALLED: + break; + } + } + + repackaged_pkg.clear(); + repackaged_cert.clear(); + switch (check_orig(user)) { + case INSTALLED: { + int uid = get_pkg_uid(user, JAVA_PACKAGE_NAME); + if (uid < 0) { + if (pkg) pkg->clear(); + return -1; + } else { + if (pkg) *pkg = JAVA_PACKAGE_NAME; + return uid; + } + } + case CERT_MISMATCH: + install = true; + case NO_INSTALLED: + break; + } + + auto xml = "/data/system/packages.xml"; + stat(xml, &st); + user_to_check[user] = make_pair(xml, st.st_ctim.tv_sec); + + if (install) install_stub(); if (pkg) pkg->clear(); return -1; } diff --git a/native/src/core/su/su_daemon.cpp b/native/src/core/su/su_daemon.cpp index 8fe323842..0e5c828ad 100644 --- a/native/src/core/su/su_daemon.cpp +++ b/native/src/core/su/su_daemon.cpp @@ -80,7 +80,6 @@ void su_info::check_db() { // We need to check our manager if (access.log || access.notify) { - check_pkg_refresh(); mgr_uid = get_manager(to_user_id(eval_uid), &mgr_pkg, true); } } diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index 24ec964eb..6affcd8e7 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -128,12 +128,10 @@ static void get_process_info(int client, const sock_cred *cred) { uint32_t flags = 0; - check_pkg_refresh(); if (is_deny_target(uid, process)) { flags |= PROCESS_ON_DENYLIST; } - int manager_app_id = get_manager(); - if (to_app_id(uid) == manager_app_id) { + if (get_manager(to_user_id(uid)) == uid) { flags |= PROCESS_IS_MAGISK_APP; } if (denylist_enforced) {