2022-05-18 01:55:58 -07:00
|
|
|
#include <base.hpp>
|
2023-11-08 01:46:02 -08:00
|
|
|
#include <consts.hpp>
|
|
|
|
#include <core.hpp>
|
2022-05-18 01:55:58 -07:00
|
|
|
#include <db.hpp>
|
2022-05-20 04:37:37 -07:00
|
|
|
#include <flags.h>
|
2022-05-18 01:55:58 -07:00
|
|
|
|
|
|
|
using namespace std;
|
2023-06-16 02:33:01 -07:00
|
|
|
using rust::Vec;
|
2022-05-18 01:55:58 -07:00
|
|
|
|
2022-05-24 05:21:36 -07:00
|
|
|
#define ENFORCE_SIGNATURE (!MAGISK_DEBUG)
|
|
|
|
|
2022-05-18 01:55:58 -07:00
|
|
|
// These functions will be called on every single zygote process specialization and su request,
|
|
|
|
// so performance is absolutely critical. Most operations should either have its result cached
|
|
|
|
// or simply skipped unless necessary.
|
|
|
|
|
2024-12-25 23:01:00 -08:00
|
|
|
struct file_info {
|
|
|
|
const string path;
|
|
|
|
|
|
|
|
file_info() = default;
|
|
|
|
file_info(string path, const struct stat *st) : path(std::move(path)) {
|
|
|
|
memcpy(×tamp, &st->st_ctim, sizeof(timestamp));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_same() const {
|
|
|
|
if (path.empty()) return false;
|
|
|
|
struct stat st{};
|
|
|
|
if (stat(path.data(), &st) != 0) return false;
|
|
|
|
return timestamp.tv_sec == st.st_ctim.tv_sec && timestamp.tv_nsec == st.st_ctim.tv_nsec;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
timespec timestamp{};
|
|
|
|
};
|
|
|
|
|
2022-05-18 01:55:58 -07:00
|
|
|
static pthread_mutex_t pkg_lock = PTHREAD_MUTEX_INITIALIZER;
|
2022-05-19 22:54:49 -07:00
|
|
|
// pkg_lock protects all following variables
|
|
|
|
static int stub_apk_fd = -1;
|
2024-02-01 04:33:35 +08:00
|
|
|
static int repackaged_app_id = -1; // Only used by dyn
|
|
|
|
static string repackaged_pkg;
|
|
|
|
static Vec<uint8_t> repackaged_cert;
|
|
|
|
static Vec<uint8_t> trusted_cert;
|
2024-12-25 23:01:00 -08:00
|
|
|
static map<int, file_info> tracked_files;
|
2024-02-01 04:33:35 +08:00
|
|
|
enum status {
|
|
|
|
INSTALLED,
|
2024-12-25 23:01:00 -08:00
|
|
|
NOT_INSTALLED,
|
2024-02-01 04:33:35 +08:00
|
|
|
CERT_MISMATCH,
|
|
|
|
};
|
2022-05-18 01:55:58 -07:00
|
|
|
|
2023-06-16 02:33:01 -07:00
|
|
|
static bool operator==(const Vec<uint8_t> &a, const Vec<uint8_t> &b) {
|
|
|
|
return a.size() == b.size() && memcmp(a.data(), b.data(), a.size()) == 0;
|
2023-06-14 22:24:42 +08:00
|
|
|
}
|
|
|
|
|
2022-05-18 01:55:58 -07:00
|
|
|
// app_id = app_no + AID_APP_START
|
|
|
|
// app_no range: [0, 9999]
|
|
|
|
vector<bool> get_app_no_list() {
|
|
|
|
vector<bool> list;
|
|
|
|
auto data_dir = xopen_dir(APP_DATA_DIR);
|
|
|
|
if (!data_dir)
|
|
|
|
return list;
|
|
|
|
dirent *entry;
|
|
|
|
while ((entry = xreaddir(data_dir.get()))) {
|
|
|
|
// For each user
|
|
|
|
int dfd = xopenat(dirfd(data_dir.get()), entry->d_name, O_RDONLY);
|
|
|
|
if (auto dir = xopen_dir(dfd)) {
|
|
|
|
while ((entry = xreaddir(dir.get()))) {
|
|
|
|
// For each package
|
|
|
|
struct stat st{};
|
|
|
|
xfstatat(dfd, entry->d_name, &st, 0);
|
|
|
|
int app_id = to_app_id(st.st_uid);
|
|
|
|
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
|
|
|
int app_no = app_id - AID_APP_START;
|
|
|
|
if (list.size() <= app_no) {
|
|
|
|
list.resize(app_no + 1);
|
|
|
|
}
|
|
|
|
list[app_no] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
close(dfd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2022-05-19 22:54:49 -07:00
|
|
|
void preserve_stub_apk() {
|
|
|
|
mutex_guard g(pkg_lock);
|
2023-11-02 15:50:36 -07:00
|
|
|
string stub_path = get_magisk_tmp() + "/stub.apk"s;
|
2022-05-19 22:54:49 -07:00
|
|
|
stub_apk_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC);
|
|
|
|
unlink(stub_path.data());
|
2024-02-01 04:33:35 +08:00
|
|
|
trusted_cert = read_certificate(stub_apk_fd, MAGISK_VER_CODE);
|
2022-05-20 04:37:37 -07:00
|
|
|
lseek(stub_apk_fd, 0, SEEK_SET);
|
2022-05-19 22:54:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void install_stub() {
|
|
|
|
if (stub_apk_fd < 0)
|
|
|
|
return;
|
|
|
|
struct stat st{};
|
|
|
|
fstat(stub_apk_fd, &st);
|
|
|
|
char apk[] = "/data/stub.apk";
|
|
|
|
int dfd = xopen(apk, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
|
|
|
|
xsendfile(dfd, stub_apk_fd, nullptr, st.st_size);
|
|
|
|
lseek(stub_apk_fd, 0, SEEK_SET);
|
|
|
|
close(dfd);
|
|
|
|
install_apk(apk);
|
|
|
|
}
|
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
static status check_dyn(int user, string &pkg) {
|
2022-05-18 01:55:58 -07:00
|
|
|
struct stat st{};
|
2024-02-01 04:33:35 +08:00
|
|
|
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");
|
2024-12-25 23:01:00 -08:00
|
|
|
return NOT_INSTALLED;
|
2024-02-01 04:33:35 +08:00
|
|
|
}
|
|
|
|
auto cert = read_certificate(dyn, MAGISK_VER_CODE);
|
|
|
|
fstat(dyn, &st);
|
|
|
|
close(dyn);
|
2022-05-18 01:55:58 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
if (cert.empty() || cert != trusted_cert) {
|
|
|
|
LOGE("pkg: dyn APK signature mismatch: %s\n", apk);
|
2022-05-24 05:39:16 -07:00
|
|
|
#if ENFORCE_SIGNATURE
|
2024-02-01 04:33:35 +08:00
|
|
|
clear_pkg(pkg.data(), user);
|
|
|
|
return CERT_MISMATCH;
|
2022-05-24 05:39:16 -07:00
|
|
|
#endif
|
2024-02-01 04:33:35 +08:00
|
|
|
}
|
2022-05-24 05:21:36 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
repackaged_app_id = to_app_id(st.st_uid);
|
2024-12-25 23:01:00 -08:00
|
|
|
tracked_files.erase(user);
|
|
|
|
tracked_files.try_emplace(user, apk, &st);
|
2024-02-01 04:33:35 +08:00
|
|
|
return INSTALLED;
|
|
|
|
}
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
static status check_stub(int user, string &pkg) {
|
|
|
|
struct stat st{};
|
|
|
|
byte_array<PATH_MAX> 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)
|
2024-12-25 23:01:00 -08:00
|
|
|
return NOT_INSTALLED;
|
2024-02-01 04:33:35 +08:00
|
|
|
auto cert = read_certificate(fd, -1);
|
|
|
|
fstat(fd, &st);
|
|
|
|
close(fd);
|
2024-01-28 00:42:43 -08:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
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;
|
|
|
|
}
|
2022-05-18 01:55:58 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
repackaged_pkg.swap(pkg);
|
|
|
|
repackaged_cert.swap(cert);
|
2024-12-25 23:01:00 -08:00
|
|
|
tracked_files.erase(user);
|
|
|
|
tracked_files.try_emplace(user, apk, &st);
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
return INSTALLED;
|
|
|
|
}
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
static status check_orig(int user) {
|
|
|
|
struct stat st{};
|
|
|
|
byte_array<PATH_MAX> 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)
|
2024-12-25 23:01:00 -08:00
|
|
|
return NOT_INSTALLED;
|
2024-02-01 04:33:35 +08:00
|
|
|
auto cert = read_certificate(fd, MAGISK_VER_CODE);
|
|
|
|
fstat(fd, &st);
|
|
|
|
close(fd);
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
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
|
|
|
|
}
|
2022-05-20 04:37:37 -07:00
|
|
|
|
2024-12-25 23:01:00 -08:00
|
|
|
tracked_files.erase(user);
|
|
|
|
tracked_files.try_emplace(user, apk, &st);
|
2024-02-01 04:33:35 +08:00
|
|
|
return INSTALLED;
|
|
|
|
}
|
2022-05-20 04:37:37 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
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;
|
|
|
|
}
|
2022-05-20 04:37:37 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
int get_manager(int user, string *pkg, bool install) {
|
|
|
|
mutex_guard g(pkg_lock);
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-12-25 23:01:00 -08:00
|
|
|
db_strings str;
|
|
|
|
get_db_strings(str, SU_MANAGER);
|
2024-12-31 22:52:34 -08:00
|
|
|
string db_pkg(std::move(str.su_manager));
|
2024-12-25 23:01:00 -08:00
|
|
|
|
|
|
|
// If database changed, always re-check files
|
|
|
|
if (db_pkg != repackaged_pkg) {
|
|
|
|
tracked_files.erase(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto &file = tracked_files[user];
|
|
|
|
if (file.is_same()) {
|
2024-02-01 04:33:35 +08:00
|
|
|
// no APK
|
2024-12-25 23:01:00 -08:00
|
|
|
if (file.path == "/data/system/packages.xml") {
|
2024-02-01 04:33:35 +08:00
|
|
|
if (install) install_stub();
|
|
|
|
if (pkg) pkg->clear();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// dyn APK is still the same
|
2024-12-25 23:01:00 -08:00
|
|
|
if (file.path.starts_with(APP_DATA_DIR)) {
|
2024-02-01 04:33:35 +08:00
|
|
|
if (pkg) *pkg = repackaged_pkg;
|
|
|
|
return user * AID_USER_OFFSET + repackaged_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 {
|
|
|
|
if (pkg) pkg->clear();
|
|
|
|
return -1;
|
2022-05-19 02:39:57 -07:00
|
|
|
}
|
|
|
|
}
|
2024-02-01 04:33:35 +08:00
|
|
|
// 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 uid;
|
|
|
|
}
|
|
|
|
}
|
2022-05-19 02:39:57 -07:00
|
|
|
|
2024-12-25 23:01:00 -08:00
|
|
|
if (!db_pkg.empty()) {
|
|
|
|
switch (check_stub(user, db_pkg)) {
|
2024-02-01 04:33:35 +08:00
|
|
|
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;
|
2022-05-20 04:37:37 -07:00
|
|
|
}
|
2024-02-01 04:33:35 +08:00
|
|
|
case CERT_MISMATCH:
|
|
|
|
install = true;
|
2024-12-25 23:01:00 -08:00
|
|
|
case NOT_INSTALLED:
|
|
|
|
rm_db_strings(SU_MANAGER);
|
2024-02-01 04:33:35 +08:00
|
|
|
break;
|
2022-05-19 02:39:57 -07:00
|
|
|
}
|
2024-02-01 04:33:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2022-05-18 01:55:58 -07:00
|
|
|
}
|
|
|
|
}
|
2024-02-01 04:33:35 +08:00
|
|
|
case CERT_MISMATCH:
|
|
|
|
install = true;
|
2024-12-25 23:01:00 -08:00
|
|
|
case NOT_INSTALLED:
|
2024-02-01 04:33:35 +08:00
|
|
|
break;
|
2022-05-19 02:39:57 -07:00
|
|
|
}
|
2022-05-18 01:55:58 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
auto xml = "/data/system/packages.xml";
|
2024-12-25 23:01:00 -08:00
|
|
|
struct stat st{};
|
2024-02-01 04:33:35 +08:00
|
|
|
stat(xml, &st);
|
2024-12-25 23:01:00 -08:00
|
|
|
tracked_files.erase(user);
|
|
|
|
tracked_files.try_emplace(user, xml, &st);
|
2022-05-20 04:37:37 -07:00
|
|
|
|
2024-02-01 04:33:35 +08:00
|
|
|
if (install) install_stub();
|
2022-05-19 02:39:57 -07:00
|
|
|
if (pkg) pkg->clear();
|
2022-05-18 01:55:58 -07:00
|
|
|
return -1;
|
|
|
|
}
|