diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index b2e25562e..76d6f87d7 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -38,7 +38,7 @@
Magisk\'in son versiyası yüklənib
- Magisk Menecer\'in son versiyası yüklənib
+ Magisk Manager\'in son versiyası yüklənib
Qabaqcıl Parametrlər
@@ -148,7 +148,7 @@
Tənzimləmələri saxlamaq üçün yenidən başladın.
- Buraxılış qeydləri
+ Yeniliklər
Repo keşi silindi
@@ -158,11 +158,11 @@
DTBO yamaqlanıb!
- Magisk Menecer dtbo.img\'ni yamaqladı. Xahiş olunur yenidən başladın.
+ Magisk Manager dtbo.img\'ni yamaqladı. Xahiş olunur yenidən başladın.
Qurulur
- Magisk Manageri\'i gizlədilir…
+ Magisk Manager gizlədilir…
Magisk Manager\'i gizlətmək alınmadı.
@@ -224,11 +224,11 @@
Magisk Manager\'i Gizlə
- Magisk Manageri\'i təsadüfi adla yenidən sıxışdır.
+ Magisk Manager\'i təsadüfi adla yenidən sıxışdır.
Magisk Manager\'i Geri Qaytar
- Magisk Manageri\'i orjinal sıxışdırma ilə geri qaytar
+ Magisk Manager\'i orjinal sıxışdırma ilə geri qaytar
Dil
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index c33451003..7390d169d 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -6,7 +6,7 @@
Superuser
Záznamy
Nastavenia
- Inštaluj
+ Inštalovať
Nepodporovaná verzia Magisku
Táto verzia Magisk Managera podporuje Magisk od verzie v18.0.\n\nBuď upgradujte Magisk manuálne alebo nainštalujte staršiu verziu aplikácie.
@@ -208,7 +208,7 @@
PID:%1$d
- Target UID: %1$d
+ Cieľový UID: %1$d
Príkaz: %1$s
Zobraziť systémové aplikácie
diff --git a/build.gradle b/build.gradle
index 6ca2511e7..203c0cdf3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,6 @@ buildscript {
maven { url 'https://kotlin.bintray.com/kotlinx' }
}
dependencies {
- classpath 'com.android.tools:r8:1.4.96'
classpath 'com.android.tools.build:gradle:3.5.0-beta02'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 1e7f43615..901b4bcf9 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -41,6 +41,7 @@ LOCAL_SRC_FILES := \
magiskhide/magiskhide.cpp \
magiskhide/proc_monitor.cpp \
magiskhide/hide_utils.cpp \
+ magiskhide/hide_policy.cpp \
resetprop/persist_properties.cpp \
resetprop/resetprop.cpp \
resetprop/system_property_api.cpp \
@@ -79,7 +80,10 @@ LOCAL_C_INCLUDES := \
$(LIBUTILS)
LOCAL_SRC_FILES := \
- core/init.cpp \
+ init/init.cpp \
+ init/early_mount.cpp \
+ init/rootfs.cpp \
+ init/getinfo.cpp \
magiskpolicy/api.cpp \
magiskpolicy/magiskpolicy.cpp \
magiskpolicy/rules.cpp \
diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp
index e592c0feb..7970cfb22 100644
--- a/native/jni/core/bootstages.cpp
+++ b/native/jni/core/bootstages.cpp
@@ -525,7 +525,7 @@ static void dump_logs() {
rename(LOGFILE, LOGFILE ".bak");
log_dump = true;
// Start a daemon thread and wait indefinitely
- new_daemon_thread([](auto) -> void* {
+ new_daemon_thread([]() -> void {
int fd = xopen(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
exec_t exec {
.fd = fd,
@@ -535,10 +535,9 @@ static void dump_logs() {
close(fd);
if (pid < 0) {
log_dump = false;
- return nullptr;
+ } else {
+ waitpid(pid, nullptr, 0);
}
- waitpid(pid, nullptr, 0);
- return nullptr;
});
}
@@ -695,6 +694,14 @@ void late_start(int client) {
exec_command_sync("/system/bin/reboot");
}
+ if (access(BBPATH, F_OK) != 0){
+ LOGE("* post-fs-data mode is not triggered\n");
+ unlock_blocks();
+ magisk_env();
+ prepare_modules();
+ close(xopen(DISABLEFILE, O_RDONLY | O_CREAT | O_CLOEXEC, 0));
+ }
+
auto_start_magiskhide();
// Run scripts after full patch, most reliable way to run scripts
diff --git a/native/jni/core/init.cpp b/native/jni/core/init.cpp
deleted file mode 100644
index e7f160a5b..000000000
--- a/native/jni/core/init.cpp
+++ /dev/null
@@ -1,749 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "binaries.h"
-#ifdef USE_64BIT
-#include "binaries_arch64.h"
-#define LIBNAME "lib64"
-#else
-#include "binaries_arch.h"
-#define LIBNAME "lib"
-#endif
-#include "magiskrc.h"
-
-using namespace std;
-
-#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
-
-#ifdef MAGISK_DEBUG
-static FILE *kmsg;
-static int vprintk(const char *fmt, va_list ap) {
- fprintf(kmsg, "magiskinit: ");
- return vfprintf(kmsg, fmt, ap);
-}
-
-static void setup_klog() {
- mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11));
- int fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC);
- kmsg = fdopen(fd, "w");
- setbuf(kmsg, nullptr);
- unlink("/kmsg");
- log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
- log_cb.ex = nop_ex;
-}
-#else
-#define setup_klog(...)
-#endif
-
-static int test_main(int argc, char *argv[]);
-
-constexpr const char *init_applet[] =
- { "magiskpolicy", "supolicy", "init_test", nullptr };
-constexpr int (*init_applet_main[])(int, char *[]) =
- { magiskpolicy_main, magiskpolicy_main, test_main, nullptr };
-
-struct cmdline {
- bool system_as_root;
- char slot[3];
- char dt_dir[128];
-};
-
-struct raw_data {
- void *buf;
- size_t sz;
-};
-
-static bool unxz(int fd, const uint8_t *buf, size_t size) {
- uint8_t out[8192];
- xz_crc32_init();
- struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);
- struct xz_buf b = {
- .in = buf,
- .in_pos = 0,
- .in_size = size,
- .out = out,
- .out_pos = 0,
- .out_size = sizeof(out)
- };
- enum xz_ret ret;
- do {
- ret = xz_dec_run(dec, &b);
- if (ret != XZ_OK && ret != XZ_STREAM_END)
- return false;
- write(fd, out, b.out_pos);
- b.out_pos = 0;
- } while (b.in_pos != size);
- return true;
-}
-
-static void decompress_ramdisk() {
- constexpr char tmp[] = "tmp.cpio";
- constexpr char ramdisk_xz[] = "ramdisk.cpio.xz";
- if (access(ramdisk_xz, F_OK))
- return;
- LOGD("Decompressing ramdisk from %s\n", ramdisk_xz);
- uint8_t *buf;
- size_t sz;
- mmap_ro(ramdisk_xz, buf, sz);
- int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
- unxz(fd, buf, sz);
- munmap(buf, sz);
- close(fd);
- cpio_mmap cpio(tmp);
- cpio.extract();
- unlink(tmp);
- unlink(ramdisk_xz);
-}
-
-static int dump_magisk(const char *path, mode_t mode) {
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
- if (fd < 0)
- return 1;
- if (!unxz(fd, magisk_xz, sizeof(magisk_xz)))
- return 1;
- close(fd);
- return 0;
-}
-
-static int dump_manager(const char *path, mode_t mode) {
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
- if (fd < 0)
- return 1;
- if (!unxz(fd, manager_xz, sizeof(manager_xz)))
- return 1;
- close(fd);
- return 0;
-}
-
-class MagiskInit {
-private:
- cmdline cmd{};
- raw_data self{};
- raw_data config{};
- int root = -1;
- char **argv;
- bool load_sepol = false;
- bool mnt_system = false;
- bool mnt_vendor = false;
- bool mnt_product = false;
- bool mnt_odm = false;
-
- void load_kernel_info();
- void preset();
- void early_mount();
- void setup_rootfs();
- bool read_dt_fstab(const char *name, char *partname, char *partfs);
- bool patch_sepolicy();
- void cleanup();
- void re_exec_init();
-
-public:
- explicit MagiskInit(char *argv[]) : argv(argv) {}
- void start();
- void test();
-};
-
-static inline void parse_cmdline(const std::function &fn) {
- char cmdline[4096];
- int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
- cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
- close(fd);
-
- char *tok, *eql, *tmp, *saveptr;
- saveptr = cmdline;
- while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) {
- eql = strchr(tok, '=');
- if (eql) {
- *eql = '\0';
- if (eql[1] == '"') {
- tmp = strchr(saveptr, '"');
- if (tmp != nullptr) {
- *tmp = '\0';
- saveptr[-1] = ' ';
- saveptr = tmp + 1;
- eql++;
- }
- }
- fn(tok, eql + 1);
- } else {
- fn(tok, "");
- }
- }
-}
-
-#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
-
-static bool check_key_combo() {
- uint8_t bitmask[(KEY_MAX + 1) / 8];
- vector events;
- constexpr const char *name = "/event";
-
- for (int minor = 64; minor < 96; ++minor) {
- if (mknod(name, S_IFCHR | 0444, makedev(13, minor))) {
- PLOGE("mknod");
- continue;
- }
- int fd = open(name, O_RDONLY | O_CLOEXEC);
- unlink(name);
- if (fd < 0)
- continue;
- memset(bitmask, 0, sizeof(bitmask));
- ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
- if (test_bit(KEY_VOLUMEUP, bitmask))
- events.push_back(fd);
- }
-
- if (events.empty())
- return false;
-
- RunFinally fin([&]() -> void {
- for (const int &fd : events)
- close(fd);
- });
-
- // Return true if volume key up is hold for more than 3 seconds
- int count = 0;
- for (int i = 0; i < 500; ++i) {
- for (const int &fd : events) {
- memset(bitmask, 0, sizeof(bitmask));
- ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
- if (test_bit(KEY_VOLUMEUP, bitmask)) {
- count++;
- break;
- }
- }
- if (count >= 300) {
- LOGD("KEY_VOLUMEUP detected: disable system-as-root\n");
- return true;
- }
- // Check every 10ms
- usleep(10000);
- }
- return false;
-}
-
-void MagiskInit::load_kernel_info() {
- // Communicate with kernel using procfs and sysfs
- xmkdir("/proc", 0755);
- xmount("proc", "/proc", "proc", 0, nullptr);
- xmkdir("/sys", 0755);
- xmount("sysfs", "/sys", "sysfs", 0, nullptr);
-
- bool enter_recovery = false;
- bool kirin = false;
- bool recovery_mode = false;
-
- parse_cmdline([&](auto key, auto value) -> void {
- LOGD("cmdline: [%s]=[%s]\n", key.data(), value);
- if (key == "androidboot.slot_suffix") {
- strcpy(cmd.slot, value);
- } else if (key == "androidboot.slot") {
- cmd.slot[0] = '_';
- strcpy(cmd.slot + 1, value);
- } else if (key == "skip_initramfs") {
- cmd.system_as_root = true;
- } else if (key == "androidboot.android_dt_dir") {
- strcpy(cmd.dt_dir, value);
- } else if (key == "enter_recovery") {
- enter_recovery = value[0] == '1';
- } else if (key == "androidboot.hardware") {
- kirin = strstr(value, "kirin") || strstr(value, "hi3660") || strstr(value, "hi6250");
- }
- });
-
- parse_prop_file("/.backup/.magisk", [&](auto key, auto value) -> bool {
- if (key == "RECOVERYMODE" && value == "true")
- recovery_mode = true;
- return true;
- });
-
- if (kirin && enter_recovery) {
- // Inform that we are actually booting as recovery
- if (!recovery_mode) {
- if (FILE *f = fopen("/.backup/.magisk", "ae"); f) {
- fprintf(f, "RECOVERYMODE=true\n");
- fclose(f);
- }
- recovery_mode = true;
- }
- }
-
- if (recovery_mode) {
- LOGD("Running in recovery mode, waiting for key...\n");
- cmd.system_as_root = !check_key_combo();
- }
-
- if (cmd.dt_dir[0] == '\0')
- strcpy(cmd.dt_dir, DEFAULT_DT_DIR);
-
- LOGD("system_as_root=[%d]\n", cmd.system_as_root);
- LOGD("slot=[%s]\n", cmd.slot);
- LOGD("dt_dir=[%s]\n", cmd.dt_dir);
-}
-
-void MagiskInit::preset() {
- root = open("/", O_RDONLY | O_CLOEXEC);
-
- if (cmd.system_as_root) {
- // Clear rootfs
- LOGD("Cleaning rootfs\n");
- frm_rf(root, { "overlay", "proc", "sys" });
- } else {
- decompress_ramdisk();
-
- // Revert original init binary
- rename("/.backup/init", "/init");
- rm_rf("/.backup");
-
- // Do not go further if device is booting into recovery
- if (access("/sbin/recovery", F_OK) == 0) {
- LOGD("Ramdisk is recovery, abort\n");
- re_exec_init();
- }
- }
-}
-
-struct device {
- int major;
- int minor;
- char devname[32];
- char partname[32];
-};
-
-static inline void parse_device(device *dev, const char *uevent) {
- dev->partname[0] = '\0';
- FILE *fp = xfopen(uevent, "re");
- char buf[64];
- while (fgets(buf, sizeof(buf), fp)) {
- if (strncmp(buf, "MAJOR", 5) == 0) {
- sscanf(buf, "MAJOR=%d", &dev->major);
- } else if (strncmp(buf, "MINOR", 5) == 0) {
- sscanf(buf, "MINOR=%d", &dev->minor);
- } else if (strncmp(buf, "DEVNAME", 7) == 0) {
- sscanf(buf, "DEVNAME=%s", dev->devname);
- } else if (strncmp(buf, "PARTNAME", 8) == 0) {
- sscanf(buf, "PARTNAME=%s", dev->partname);
- }
- }
- fclose(fp);
-}
-
-static vector dev_list;
-
-static void collect_devices() {
- char path[128];
- struct dirent *entry;
- device dev;
- DIR *dir = xopendir("/sys/dev/block");
- if (dir == nullptr)
- return;
- while ((entry = readdir(dir))) {
- if (entry->d_name == "."sv || entry->d_name == ".."sv)
- continue;
- sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
- parse_device(&dev, path);
- dev_list.push_back(dev);
- }
- closedir(dir);
-}
-
-static bool setup_block(const char *partname, char *block_dev) {
- if (dev_list.empty())
- collect_devices();
- for (auto &dev : dev_list) {
- if (strcasecmp(dev.partname, partname) == 0) {
- sprintf(block_dev, "/dev/block/%s", dev.devname);
- LOGD("Found %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
- xmkdir("/dev", 0755);
- xmkdir("/dev/block", 0755);
- mknod(block_dev, S_IFBLK | 0600, makedev(dev.major, dev.minor));
- return true;
- }
- }
- return false;
-}
-
-bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *partfs) {
- char path[128];
- int fd;
- sprintf(path, "%s/fstab/%s/dev", cmd.dt_dir, name);
- if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) {
- read(fd, path, sizeof(path));
- close(fd);
- // Some custom treble use different names, so use what we read
- char *part = rtrim(strrchr(path, '/') + 1);
- sprintf(partname, "%s%s", part, strend(part, cmd.slot) ? cmd.slot : "");
- sprintf(path, "%s/fstab/%s/type", cmd.dt_dir, name);
- if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) {
- read(fd, partfs, 32);
- close(fd);
- return true;
- }
- }
- return false;
-}
-
-static inline bool is_lnk(const char *name) {
- struct stat st;
- if (lstat(name, &st))
- return false;
- return S_ISLNK(st.st_mode);
-}
-
-#define link_root(name) \
-if (is_lnk("/system_root" name)) \
- cp_afc("/system_root" name, name)
-
-#define mount_root(name) \
-if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \
- LOGD("Early mount " #name "\n"); \
- setup_block(partname, block_dev); \
- xmkdir("/" #name, 0755); \
- xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \
- mnt_##name = true; \
-}
-
-void MagiskInit::early_mount() {
- char partname[32];
- char fstype[32];
- char block_dev[64];
-
- if (cmd.system_as_root) {
- LOGD("Early mount system_root\n");
- sprintf(partname, "system%s", cmd.slot);
- setup_block(partname, block_dev);
- xmkdir("/system_root", 0755);
- xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr);
- xmkdir("/system", 0755);
- xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr);
-
- // Android Q
- if (is_lnk("/system_root/init"))
- load_sepol = true;
-
- // System-as-root with monolithic sepolicy
- if (access("/system_root/sepolicy", F_OK) == 0)
- cp_afc("/system_root/sepolicy", "/sepolicy");
-
- // Copy if these partitions are symlinks
- link_root("/vendor");
- link_root("/product");
- link_root("/odm");
- } else {
- mount_root(system);
- }
-
- mount_root(vendor);
- mount_root(product);
- mount_root(odm);
-}
-
-static void patch_socket_name(const char *path) {
- uint8_t *buf;
- char name[sizeof(MAIN_SOCKET)];
- size_t size;
- mmap_rw(path, buf, size);
- for (int i = 0; i < size; ++i) {
- if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
- gen_rand_str(name, sizeof(name));
- memcpy(buf + i, name, sizeof(name));
- i += sizeof(name);
- }
- }
- munmap(buf, size);
-}
-
-constexpr const char wrapper[] =
-"#!/system/bin/sh\n"
-"export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:/apex/com.android.runtime/" LIBNAME "\"\n"
-"exec /sbin/magisk.bin \"$0\" \"$@\"\n"
-;
-
-void MagiskInit::setup_rootfs() {
- bool patch_init = patch_sepolicy();
-
- if (cmd.system_as_root) {
- // Clone rootfs
- LOGD("Clone root dir from system to rootfs\n");
- int system_root = xopen("/system_root", O_RDONLY | O_CLOEXEC);
- clone_dir(system_root, root, false);
- close(system_root);
- }
-
- if (patch_init) {
- constexpr char SYSTEM_INIT[] = "/system/bin/init";
- // If init is symlink, copy it to rootfs so we can patch
- if (is_lnk("/init"))
- cp_afc(SYSTEM_INIT, "/init");
-
- char *addr;
- size_t size;
- mmap_rw("/init", addr, size);
- for (char *p = addr; p < addr + size; ++p) {
- if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) {
- // Force init to load /sepolicy
- LOGD("Remove from init: " SPLIT_PLAT_CIL "\n");
- memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1);
- p += sizeof(SPLIT_PLAT_CIL) - 1;
- } else if (memcmp(p, SYSTEM_INIT, sizeof(SYSTEM_INIT)) == 0) {
- // Force execute /init instead of /system/bin/init
- LOGD("Patch init: [/system/bin/init] -> [/init]\n");
- strcpy(p, "/init");
- p += sizeof(SYSTEM_INIT) - 1;
- }
- }
- munmap(addr, size);
- }
-
- // Handle ramdisk overlays
- int fd = open("/overlay", O_RDONLY | O_CLOEXEC);
- if (fd >= 0) {
- LOGD("Merge overlay folder\n");
- mv_dir(fd, root);
- close(fd);
- rmdir("/overlay");
- }
-
- // Patch init.rc
- FILE *rc = xfopen("/init.p.rc", "we");
- file_readline("/init.rc", [&](auto line) -> bool {
- // Do not start vaultkeeper
- if (str_contains(line, "start vaultkeeper")) {
- LOGD("Remove vaultkeeper\n");
- return true;
- }
- // Do not run flash_recovery
- if (str_starts(line, "service flash_recovery")) {
- LOGD("Remove flash_recovery\n");
- fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
- return true;
- }
- // Else just write the line
- fprintf(rc, "%s", line.data());
- return true;
- });
- char pfd_svc[8], ls_svc[8], bc_svc[8];
- // Make sure to be unique
- pfd_svc[0] = 'a';
- ls_svc[0] = '0';
- bc_svc[0] = 'A';
- gen_rand_str(pfd_svc + 1, sizeof(pfd_svc) - 1);
- gen_rand_str(ls_svc + 1, sizeof(ls_svc) - 1);
- gen_rand_str(bc_svc + 1, sizeof(bc_svc) - 1);
- LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc);
- fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc, bc_svc, bc_svc);
- fclose(rc);
- clone_attr("/init.rc", "/init.p.rc");
- rename("/init.p.rc", "/init.rc");
-
- // Don't let init run in init yet
- lsetfilecon("/init", "u:object_r:rootfs:s0");
-
- // Create hardlink mirror of /sbin to /root
- mkdir("/root", 0750);
- clone_attr("/sbin", "/root");
- int rootdir = xopen("/root", O_RDONLY | O_CLOEXEC);
- int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
- link_dir(sbin, rootdir);
- close(sbin);
-
- LOGD("Mount /sbin tmpfs overlay\n");
- xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
- sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
-
- char path[64];
-
- // Create symlinks pointing back to /root
- DIR *dir = xfdopendir(rootdir);
- struct dirent *entry;
- while((entry = xreaddir(dir))) {
- if (entry->d_name == "."sv || entry->d_name == ".."sv)
- continue;
- sprintf(path, "/root/%s", entry->d_name);
- xsymlinkat(path, sbin, entry->d_name);
- }
-
- // Dump binaries
- mkdir(MAGISKTMP, 0755);
- fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000);
- write(fd, config.buf, config.sz);
- close(fd);
- fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755);
- write(fd, self.buf, self.sz);
- close(fd);
- if (access("/system/apex", F_OK) == 0) {
- LOGD("APEX detected, use wrapper\n");
- dump_magisk("/sbin/magisk.bin", 0755);
- patch_socket_name("/sbin/magisk.bin");
- fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755);
- write(fd, wrapper, sizeof(wrapper) - 1);
- close(fd);
- } else {
- dump_magisk("/sbin/magisk", 0755);
- patch_socket_name("/sbin/magisk");
- }
-
- // Create applet symlinks
- for (int i = 0; applet_names[i]; ++i) {
- sprintf(path, "/sbin/%s", applet_names[i]);
- xsymlink("/sbin/magisk", path);
- }
- xsymlink("/sbin/magiskinit", "/sbin/magiskpolicy");
- xsymlink("/sbin/magiskinit", "/sbin/supolicy");
-
- close(rootdir);
- close(sbin);
-}
-
-bool MagiskInit::patch_sepolicy() {
- bool patch_init = false;
-
- if (access(SPLIT_PLAT_CIL, R_OK) == 0) {
- LOGD("sepol: split policy\n");
- patch_init = true;
- } else if (access("/sepolicy", R_OK) == 0) {
- LOGD("sepol: monolithic policy\n");
- load_policydb("/sepolicy");
- } else {
- LOGD("sepol: no selinux\n");
- return false;
- }
-
- // Mount selinuxfs to communicate with kernel
- xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr);
-
- if (patch_init)
- load_split_cil();
-
- sepol_magisk_rules();
- sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
- dump_policydb("/sepolicy");
-
- // Load policy to kernel so we can label rootfs
- if (load_sepol) {
- LOGD("sepol: preload sepolicy\n");
- dump_policydb(SELINUX_LOAD);
- }
-
- // Remove OnePlus stupid debug sepolicy and use our own
- if (access("/sepolicy_debug", F_OK) == 0) {
- unlink("/sepolicy_debug");
- link("/sepolicy", "/sepolicy_debug");
- }
-
- // Enable selinux functions
- selinux_builtin_impl();
-
- return patch_init;
-}
-
-#define umount_root(name) \
-if (mnt_##name) \
- umount("/" #name);
-
-void MagiskInit::cleanup() {
- umount(SELINUX_MNT);
- umount("/sys");
- umount("/proc");
- umount_root(system);
- umount_root(vendor);
- umount_root(product);
- umount_root(odm);
-}
-
-void MagiskInit::re_exec_init() {
- LOGD("Re-exec /init\n");
- cleanup();
- execv("/init", argv);
- exit(1);
-}
-
-void MagiskInit::start() {
- // Prevent file descriptor confusion
- mknod("/null", S_IFCHR | 0666, makedev(1, 3));
- int null = open("/null", O_RDWR | O_CLOEXEC);
- unlink("/null");
- xdup3(null, STDIN_FILENO, O_CLOEXEC);
- xdup3(null, STDOUT_FILENO, O_CLOEXEC);
- xdup3(null, STDERR_FILENO, O_CLOEXEC);
- if (null > STDERR_FILENO)
- close(null);
-
- setup_klog();
-
- load_kernel_info();
-
- full_read("/init", &self.buf, &self.sz);
- full_read("/.backup/.magisk", &config.buf, &config.sz);
-
- preset();
- early_mount();
- setup_rootfs();
- re_exec_init();
-}
-
-void MagiskInit::test() {
- cmdline_logging();
- log_cb.ex = nop_ex;
-
- chdir(dirname(argv[0]));
- chroot(".");
- chdir("/");
-
- load_kernel_info();
- preset();
- early_mount();
- setup_rootfs();
- cleanup();
-}
-
-static int test_main(int, char *argv[]) {
- MagiskInit init(argv);
- init.test();
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- umask(0);
-
- for (int i = 0; init_applet[i]; ++i) {
- if (strcmp(basename(argv[0]), init_applet[i]) == 0)
- return (*init_applet_main[i])(argc, argv);
- }
-
- if (argc > 1 && strcmp(argv[1], "-x") == 0) {
- if (strcmp(argv[2], "magisk") == 0)
- return dump_magisk(argv[3], 0755);
- else if (strcmp(argv[2], "manager") == 0)
- return dump_manager(argv[3], 0644);
- }
-
- if (getpid() != 1)
- return 1;
-
- MagiskInit init(argv);
-
- // Run the main routine
- init.start();
-}
diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/early_mount.cpp
new file mode 100644
index 000000000..472d2bf9e
--- /dev/null
+++ b/native/jni/init/early_mount.cpp
@@ -0,0 +1,143 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "init.h"
+
+using namespace std;
+
+struct devinfo {
+ int major;
+ int minor;
+ char devname[32];
+ char partname[32];
+};
+
+static vector dev_list;
+
+static void parse_device(devinfo *dev, const char *uevent) {
+ dev->partname[0] = '\0';
+ parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
+ if (key == "MAJOR")
+ dev->major = atoi(value.data());
+ else if (key == "MINOR")
+ dev->minor = atoi(value.data());
+ else if (key == "DEVNAME")
+ strcpy(dev->devname, value.data());
+ else if (key == "PARTNAME")
+ strcpy(dev->partname, value.data());
+
+ return true;
+ });
+}
+
+static void collect_devices() {
+ char path[128];
+ struct dirent *entry;
+ devinfo dev;
+ DIR *dir = xopendir("/sys/dev/block");
+ if (dir == nullptr)
+ return;
+ while ((entry = readdir(dir))) {
+ if (entry->d_name == "."sv || entry->d_name == ".."sv)
+ continue;
+ sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
+ parse_device(&dev, path);
+ dev_list.push_back(dev);
+ }
+ closedir(dir);
+}
+
+static void setup_block(const char *partname, char *block_dev) {
+ if (dev_list.empty())
+ collect_devices();
+ for (;;) {
+ for (auto &dev : dev_list) {
+ if (strcasecmp(dev.partname, partname) == 0) {
+ sprintf(block_dev, "/dev/block/%s", dev.devname);
+ LOGD("Found %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
+ xmkdir("/dev", 0755);
+ xmkdir("/dev/block", 0755);
+ mknod(block_dev, S_IFBLK | 0600, makedev(dev.major, dev.minor));
+ return;
+ }
+ }
+ // Wait 10ms and try again
+ usleep(10000);
+ dev_list.clear();
+ collect_devices();
+ }
+}
+
+bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
+ char path[128];
+ int fd;
+ sprintf(path, "%s/fstab/%s/dev", cmd.dt_dir, name);
+ if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) {
+ read(fd, path, sizeof(path));
+ close(fd);
+ // Some custom treble use different names, so use what we read
+ char *part = rtrim(strrchr(path, '/') + 1);
+ sprintf(partname, "%s%s", part, strend(part, cmd.slot) ? cmd.slot : "");
+ sprintf(path, "%s/fstab/%s/type", cmd.dt_dir, name);
+ if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) {
+ read(fd, fstype, 32);
+ close(fd);
+ return true;
+ }
+ }
+ return false;
+}
+
+#define link_root(name) \
+if (is_lnk("/system_root" name)) \
+ cp_afc("/system_root" name, name)
+
+#define mount_root(name) \
+if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \
+ LOGD("Early mount " #name "\n"); \
+ setup_block(partname, block_dev); \
+ xmkdir("/" #name, 0755); \
+ xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \
+ mnt_##name = true; \
+}
+
+void MagiskInit::early_mount() {
+ char partname[32];
+ char fstype[32];
+ char block_dev[64];
+
+ if (cmd.system_as_root) {
+ LOGD("Early mount system_root\n");
+ sprintf(partname, "system%s", cmd.slot);
+ setup_block(partname, block_dev);
+ xmkdir("/system_root", 0755);
+ if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr))
+ xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr);
+ xmkdir("/system", 0755);
+ xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr);
+
+ // Android Q
+ if (is_lnk("/system_root/init"))
+ load_sepol = true;
+
+ // System-as-root with monolithic sepolicy
+ if (access("/system_root/sepolicy", F_OK) == 0)
+ cp_afc("/system_root/sepolicy", "/sepolicy");
+
+ // Copy if these partitions are symlinks
+ link_root("/vendor");
+ link_root("/product");
+ link_root("/odm");
+ } else {
+ mount_root(system);
+ }
+
+ mount_root(vendor);
+ mount_root(product);
+ mount_root(odm);
+}
diff --git a/native/jni/init/getinfo.cpp b/native/jni/init/getinfo.cpp
new file mode 100644
index 000000000..a90c7b916
--- /dev/null
+++ b/native/jni/init/getinfo.cpp
@@ -0,0 +1,152 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "init.h"
+
+using namespace std;
+
+#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
+
+static void parse_cmdline(const std::function &fn) {
+ char cmdline[4096];
+ int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
+ cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
+ close(fd);
+
+ char *tok, *eql, *tmp, *saveptr;
+ saveptr = cmdline;
+ while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) {
+ eql = strchr(tok, '=');
+ if (eql) {
+ *eql = '\0';
+ if (eql[1] == '"') {
+ tmp = strchr(saveptr, '"');
+ if (tmp != nullptr) {
+ *tmp = '\0';
+ saveptr[-1] = ' ';
+ saveptr = tmp + 1;
+ eql++;
+ }
+ }
+ fn(tok, eql + 1);
+ } else {
+ fn(tok, "");
+ }
+ }
+}
+
+#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
+
+static bool check_key_combo() {
+ uint8_t bitmask[(KEY_MAX + 1) / 8];
+ vector events;
+ constexpr const char *name = "/event";
+
+ for (int minor = 64; minor < 96; ++minor) {
+ if (mknod(name, S_IFCHR | 0444, makedev(13, minor))) {
+ PLOGE("mknod");
+ continue;
+ }
+ int fd = open(name, O_RDONLY | O_CLOEXEC);
+ unlink(name);
+ if (fd < 0)
+ continue;
+ memset(bitmask, 0, sizeof(bitmask));
+ ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
+ if (test_bit(KEY_VOLUMEUP, bitmask))
+ events.push_back(fd);
+ }
+
+ if (events.empty())
+ return false;
+
+ RunFinally fin([&]() -> void {
+ for (const int &fd : events)
+ close(fd);
+ });
+
+ // Return true if volume key up is hold for more than 3 seconds
+ int count = 0;
+ for (int i = 0; i < 500; ++i) {
+ for (const int &fd : events) {
+ memset(bitmask, 0, sizeof(bitmask));
+ ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
+ if (test_bit(KEY_VOLUMEUP, bitmask)) {
+ count++;
+ break;
+ }
+ }
+ if (count >= 300) {
+ LOGD("KEY_VOLUMEUP detected: disable system-as-root\n");
+ return true;
+ }
+ // Check every 10ms
+ usleep(10000);
+ }
+ return false;
+}
+
+void MagiskInit::load_kernel_info() {
+ // Communicate with kernel using procfs and sysfs
+ xmkdir("/proc", 0755);
+ xmount("proc", "/proc", "proc", 0, nullptr);
+ xmkdir("/sys", 0755);
+ xmount("sysfs", "/sys", "sysfs", 0, nullptr);
+
+ bool enter_recovery = false;
+ bool kirin = false;
+ bool recovery_mode = false;
+
+ parse_cmdline([&](auto key, auto value) -> void {
+ LOGD("cmdline: [%s]=[%s]\n", key.data(), value);
+ if (key == "androidboot.slot_suffix") {
+ strcpy(cmd.slot, value);
+ } else if (key == "androidboot.slot") {
+ cmd.slot[0] = '_';
+ strcpy(cmd.slot + 1, value);
+ } else if (key == "skip_initramfs") {
+ cmd.system_as_root = true;
+ } else if (key == "androidboot.android_dt_dir") {
+ strcpy(cmd.dt_dir, value);
+ } else if (key == "enter_recovery") {
+ enter_recovery = value[0] == '1';
+ } else if (key == "androidboot.hardware") {
+ kirin = strstr(value, "kirin") || strstr(value, "hi3660") || strstr(value, "hi6250");
+ }
+ });
+
+ parse_prop_file("/.backup/.magisk", [&](auto key, auto value) -> bool {
+ if (key == "RECOVERYMODE" && value == "true")
+ recovery_mode = true;
+ return true;
+ });
+
+ if (kirin && enter_recovery) {
+ // Inform that we are actually booting as recovery
+ if (!recovery_mode) {
+ if (FILE *f = fopen("/.backup/.magisk", "ae"); f) {
+ fprintf(f, "RECOVERYMODE=true\n");
+ fclose(f);
+ }
+ recovery_mode = true;
+ }
+ }
+
+ if (recovery_mode) {
+ LOGD("Running in recovery mode, waiting for key...\n");
+ cmd.system_as_root = !check_key_combo();
+ }
+
+ if (cmd.dt_dir[0] == '\0')
+ strcpy(cmd.dt_dir, DEFAULT_DT_DIR);
+
+ LOGD("system_as_root=[%d]\n", cmd.system_as_root);
+ LOGD("slot=[%s]\n", cmd.slot);
+ LOGD("dt_dir=[%s]\n", cmd.dt_dir);
+}
diff --git a/native/jni/init/init.cpp b/native/jni/init/init.cpp
new file mode 100644
index 000000000..80d50754a
--- /dev/null
+++ b/native/jni/init/init.cpp
@@ -0,0 +1,230 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "binaries.h"
+#ifdef USE_64BIT
+#include "binaries_arch64.h"
+#else
+#include "binaries_arch.h"
+#endif
+
+#include "init.h"
+
+using namespace std;
+
+#ifdef MAGISK_DEBUG
+static FILE *kmsg;
+static int vprintk(const char *fmt, va_list ap) {
+ fprintf(kmsg, "magiskinit: ");
+ return vfprintf(kmsg, fmt, ap);
+}
+
+static void setup_klog() {
+ mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11));
+ int fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC);
+ kmsg = fdopen(fd, "w");
+ setbuf(kmsg, nullptr);
+ unlink("/kmsg");
+ log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
+ log_cb.ex = nop_ex;
+}
+#else
+#define setup_klog(...)
+#endif
+
+static int test_main(int argc, char *argv[]);
+
+constexpr const char *init_applet[] =
+ { "magiskpolicy", "supolicy", "init_test", nullptr };
+constexpr int (*init_applet_main[])(int, char *[]) =
+ { magiskpolicy_main, magiskpolicy_main, test_main, nullptr };
+
+static bool unxz(int fd, const uint8_t *buf, size_t size) {
+ uint8_t out[8192];
+ xz_crc32_init();
+ struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ struct xz_buf b = {
+ .in = buf,
+ .in_pos = 0,
+ .in_size = size,
+ .out = out,
+ .out_pos = 0,
+ .out_size = sizeof(out)
+ };
+ enum xz_ret ret;
+ do {
+ ret = xz_dec_run(dec, &b);
+ if (ret != XZ_OK && ret != XZ_STREAM_END)
+ return false;
+ write(fd, out, b.out_pos);
+ b.out_pos = 0;
+ } while (b.in_pos != size);
+ return true;
+}
+
+static void decompress_ramdisk() {
+ constexpr char tmp[] = "tmp.cpio";
+ constexpr char ramdisk_xz[] = "ramdisk.cpio.xz";
+ if (access(ramdisk_xz, F_OK))
+ return;
+ LOGD("Decompressing ramdisk from %s\n", ramdisk_xz);
+ uint8_t *buf;
+ size_t sz;
+ mmap_ro(ramdisk_xz, buf, sz);
+ int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
+ unxz(fd, buf, sz);
+ munmap(buf, sz);
+ close(fd);
+ cpio_mmap cpio(tmp);
+ cpio.extract();
+ unlink(tmp);
+ unlink(ramdisk_xz);
+}
+
+int dump_magisk(const char *path, mode_t mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+ if (fd < 0)
+ return 1;
+ if (!unxz(fd, magisk_xz, sizeof(magisk_xz)))
+ return 1;
+ close(fd);
+ return 0;
+}
+
+static int dump_manager(const char *path, mode_t mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+ if (fd < 0)
+ return 1;
+ if (!unxz(fd, manager_xz, sizeof(manager_xz)))
+ return 1;
+ close(fd);
+ return 0;
+}
+
+void MagiskInit::preset() {
+ root = open("/", O_RDONLY | O_CLOEXEC);
+
+ if (cmd.system_as_root) {
+ // Clear rootfs
+ LOGD("Cleaning rootfs\n");
+ frm_rf(root, { "overlay", "proc", "sys" });
+ } else {
+ decompress_ramdisk();
+
+ // Revert original init binary
+ rename("/.backup/init", "/init");
+ rm_rf("/.backup");
+
+ // Do not go further if device is booting into recovery
+ if (access("/sbin/recovery", F_OK) == 0) {
+ LOGD("Ramdisk is recovery, abort\n");
+ re_exec_init();
+ }
+ }
+}
+
+#define umount_root(name) \
+if (mnt_##name) \
+ umount("/" #name);
+
+void MagiskInit::cleanup() {
+ umount(SELINUX_MNT);
+ umount("/sys");
+ umount("/proc");
+ umount_root(system);
+ umount_root(vendor);
+ umount_root(product);
+ umount_root(odm);
+}
+
+void MagiskInit::re_exec_init() {
+ LOGD("Re-exec /init\n");
+ cleanup();
+ execv("/init", argv);
+ exit(1);
+}
+
+void MagiskInit::start() {
+ // Prevent file descriptor confusion
+ mknod("/null", S_IFCHR | 0666, makedev(1, 3));
+ int null = open("/null", O_RDWR | O_CLOEXEC);
+ unlink("/null");
+ xdup3(null, STDIN_FILENO, O_CLOEXEC);
+ xdup3(null, STDOUT_FILENO, O_CLOEXEC);
+ xdup3(null, STDERR_FILENO, O_CLOEXEC);
+ if (null > STDERR_FILENO)
+ close(null);
+
+ setup_klog();
+
+ load_kernel_info();
+
+ full_read("/init", &self.buf, &self.sz);
+ full_read("/.backup/.magisk", &config.buf, &config.sz);
+
+ preset();
+ early_mount();
+ setup_rootfs();
+ re_exec_init();
+}
+
+void MagiskInit::test() {
+ cmdline_logging();
+ log_cb.ex = nop_ex;
+
+ chdir(dirname(argv[0]));
+ chroot(".");
+ chdir("/");
+
+ load_kernel_info();
+ preset();
+ early_mount();
+ setup_rootfs();
+ cleanup();
+}
+
+static int test_main(int, char *argv[]) {
+ MagiskInit init(argv);
+ init.test();
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ umask(0);
+
+ for (int i = 0; init_applet[i]; ++i) {
+ if (strcmp(basename(argv[0]), init_applet[i]) == 0)
+ return (*init_applet_main[i])(argc, argv);
+ }
+
+ if (argc > 1 && strcmp(argv[1], "-x") == 0) {
+ if (strcmp(argv[2], "magisk") == 0)
+ return dump_magisk(argv[3], 0755);
+ else if (strcmp(argv[2], "manager") == 0)
+ return dump_manager(argv[3], 0644);
+ }
+
+ if (getpid() != 1)
+ return 1;
+
+ MagiskInit init(argv);
+
+ // Run the main routine
+ init.start();
+}
diff --git a/native/jni/init/init.h b/native/jni/init/init.h
new file mode 100644
index 000000000..078651a38
--- /dev/null
+++ b/native/jni/init/init.h
@@ -0,0 +1,49 @@
+#include
+
+struct cmdline {
+ bool system_as_root;
+ char slot[3];
+ char dt_dir[128];
+};
+
+struct raw_data {
+ void *buf;
+ size_t sz;
+};
+
+class MagiskInit {
+private:
+ cmdline cmd{};
+ raw_data self{};
+ raw_data config{};
+ int root = -1;
+ char **argv;
+ bool load_sepol = false;
+ bool mnt_system = false;
+ bool mnt_vendor = false;
+ bool mnt_product = false;
+ bool mnt_odm = false;
+
+ void load_kernel_info();
+ void preset();
+ void early_mount();
+ void setup_rootfs();
+ bool read_dt_fstab(const char *name, char *partname, char *fstype);
+ bool patch_sepolicy();
+ void cleanup();
+ void re_exec_init();
+
+public:
+ explicit MagiskInit(char *argv[]) : argv(argv) {}
+ void start();
+ void test();
+};
+
+static inline bool is_lnk(const char *name) {
+ struct stat st;
+ if (lstat(name, &st))
+ return false;
+ return S_ISLNK(st.st_mode);
+}
+
+int dump_magisk(const char *path, mode_t mode);
diff --git a/native/jni/core/magiskrc.h b/native/jni/init/magiskrc.h
similarity index 100%
rename from native/jni/core/magiskrc.h
rename to native/jni/init/magiskrc.h
diff --git a/native/jni/init/rootfs.cpp b/native/jni/init/rootfs.cpp
new file mode 100644
index 000000000..609c39b3b
--- /dev/null
+++ b/native/jni/init/rootfs.cpp
@@ -0,0 +1,216 @@
+#include
+#include
+#include
+
+#include
+#include
+
+#include "init.h"
+#include "magiskrc.h"
+
+#ifdef USE_64BIT
+#define LIBNAME "lib64"
+#else
+#define LIBNAME "lib"
+#endif
+
+using namespace std;
+
+static void patch_socket_name(const char *path) {
+ uint8_t *buf;
+ char name[sizeof(MAIN_SOCKET)];
+ size_t size;
+ mmap_rw(path, buf, size);
+ for (int i = 0; i < size; ++i) {
+ if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
+ gen_rand_str(name, sizeof(name));
+ memcpy(buf + i, name, sizeof(name));
+ i += sizeof(name);
+ }
+ }
+ munmap(buf, size);
+}
+
+constexpr const char wrapper[] =
+"#!/system/bin/sh\n"
+"export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:/apex/com.android.runtime/" LIBNAME "\"\n"
+"exec /sbin/magisk.bin \"$0\" \"$@\"\n"
+;
+
+void MagiskInit::setup_rootfs() {
+ bool patch_init = patch_sepolicy();
+
+ if (cmd.system_as_root) {
+ // Clone rootfs
+ LOGD("Clone root dir from system to rootfs\n");
+ int system_root = xopen("/system_root", O_RDONLY | O_CLOEXEC);
+ clone_dir(system_root, root, false);
+ close(system_root);
+ }
+
+ if (patch_init) {
+ constexpr char SYSTEM_INIT[] = "/system/bin/init";
+ // If init is symlink, copy it to rootfs so we can patch
+ if (is_lnk("/init"))
+ cp_afc(SYSTEM_INIT, "/init");
+
+ char *addr;
+ size_t size;
+ mmap_rw("/init", addr, size);
+ for (char *p = addr; p < addr + size; ++p) {
+ if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) {
+ // Force init to load /sepolicy
+ LOGD("Remove from init: " SPLIT_PLAT_CIL "\n");
+ memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1);
+ p += sizeof(SPLIT_PLAT_CIL) - 1;
+ } else if (memcmp(p, SYSTEM_INIT, sizeof(SYSTEM_INIT)) == 0) {
+ // Force execute /init instead of /system/bin/init
+ LOGD("Patch init: [/system/bin/init] -> [/init]\n");
+ strcpy(p, "/init");
+ p += sizeof(SYSTEM_INIT) - 1;
+ }
+ }
+ munmap(addr, size);
+ }
+
+ // Handle ramdisk overlays
+ int fd = open("/overlay", O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ LOGD("Merge overlay folder\n");
+ mv_dir(fd, root);
+ close(fd);
+ rmdir("/overlay");
+ }
+
+ // Patch init.rc
+ FILE *rc = xfopen("/init.p.rc", "we");
+ file_readline("/init.rc", [&](auto line) -> bool {
+ // Do not start vaultkeeper
+ if (str_contains(line, "start vaultkeeper")) {
+ LOGD("Remove vaultkeeper\n");
+ return true;
+ }
+ // Do not run flash_recovery
+ if (str_starts(line, "service flash_recovery")) {
+ LOGD("Remove flash_recovery\n");
+ fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
+ return true;
+ }
+ // Else just write the line
+ fprintf(rc, "%s", line.data());
+ return true;
+ });
+ char pfd_svc[8], ls_svc[8], bc_svc[8];
+ // Make sure to be unique
+ pfd_svc[0] = 'a';
+ ls_svc[0] = '0';
+ bc_svc[0] = 'A';
+ gen_rand_str(pfd_svc + 1, sizeof(pfd_svc) - 1);
+ gen_rand_str(ls_svc + 1, sizeof(ls_svc) - 1);
+ gen_rand_str(bc_svc + 1, sizeof(bc_svc) - 1);
+ LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc);
+ fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc, bc_svc, bc_svc);
+ fclose(rc);
+ clone_attr("/init.rc", "/init.p.rc");
+ rename("/init.p.rc", "/init.rc");
+
+ // Don't let init run in init yet
+ lsetfilecon("/init", "u:object_r:rootfs:s0");
+
+ // Create hardlink mirror of /sbin to /root
+ mkdir("/root", 0750);
+ clone_attr("/sbin", "/root");
+ int rootdir = xopen("/root", O_RDONLY | O_CLOEXEC);
+ int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
+ link_dir(sbin, rootdir);
+ close(sbin);
+
+ LOGD("Mount /sbin tmpfs overlay\n");
+ xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
+ sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
+
+ char path[64];
+
+ // Create symlinks pointing back to /root
+ DIR *dir = xfdopendir(rootdir);
+ struct dirent *entry;
+ while((entry = xreaddir(dir))) {
+ if (entry->d_name == "."sv || entry->d_name == ".."sv)
+ continue;
+ sprintf(path, "/root/%s", entry->d_name);
+ xsymlinkat(path, sbin, entry->d_name);
+ }
+
+ // Dump binaries
+ mkdir(MAGISKTMP, 0755);
+ fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000);
+ write(fd, config.buf, config.sz);
+ close(fd);
+ fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755);
+ write(fd, self.buf, self.sz);
+ close(fd);
+ if (access("/system/apex", F_OK) == 0) {
+ LOGD("APEX detected, use wrapper\n");
+ dump_magisk("/sbin/magisk.bin", 0755);
+ patch_socket_name("/sbin/magisk.bin");
+ fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755);
+ write(fd, wrapper, sizeof(wrapper) - 1);
+ close(fd);
+ } else {
+ dump_magisk("/sbin/magisk", 0755);
+ patch_socket_name("/sbin/magisk");
+ }
+
+ // Create applet symlinks
+ for (int i = 0; applet_names[i]; ++i) {
+ sprintf(path, "/sbin/%s", applet_names[i]);
+ xsymlink("/sbin/magisk", path);
+ }
+ xsymlink("/sbin/magiskinit", "/sbin/magiskpolicy");
+ xsymlink("/sbin/magiskinit", "/sbin/supolicy");
+
+ close(rootdir);
+ close(sbin);
+}
+
+bool MagiskInit::patch_sepolicy() {
+ bool patch_init = false;
+
+ if (access(SPLIT_PLAT_CIL, R_OK) == 0) {
+ LOGD("sepol: split policy\n");
+ patch_init = true;
+ } else if (access("/sepolicy", R_OK) == 0) {
+ LOGD("sepol: monolithic policy\n");
+ load_policydb("/sepolicy");
+ } else {
+ LOGD("sepol: no selinux\n");
+ return false;
+ }
+
+ // Mount selinuxfs to communicate with kernel
+ xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr);
+
+ if (patch_init)
+ load_split_cil();
+
+ sepol_magisk_rules();
+ sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
+ dump_policydb("/sepolicy");
+
+ // Load policy to kernel so we can label rootfs
+ if (load_sepol) {
+ LOGD("sepol: preload sepolicy\n");
+ dump_policydb(SELINUX_LOAD);
+ }
+
+ // Remove OnePlus stupid debug sepolicy and use our own
+ if (access("/sepolicy_debug", F_OK) == 0) {
+ unlink("/sepolicy_debug");
+ link("/sepolicy", "/sepolicy_debug");
+ }
+
+ // Enable selinux functions
+ selinux_builtin_impl();
+
+ return patch_init;
+}
diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp
new file mode 100644
index 000000000..63b0136f6
--- /dev/null
+++ b/native/jni/magiskhide/hide_policy.cpp
@@ -0,0 +1,97 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#include "magiskhide.h"
+
+using namespace std;
+
+static const char *prop_key[] =
+ { "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked",
+ "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit", "ro.debuggable",
+ "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", nullptr };
+
+static const char *prop_value[] =
+ { "locked", "green", "1",
+ "enforcing", "0", "0", "0",
+ "1", "user", "release-keys", "0", nullptr };
+
+void hide_sensitive_props() {
+ LOGI("hide_policy: Hiding sensitive props\n");
+
+ // Hide all sensitive props
+ for (int i = 0; prop_key[i]; ++i) {
+ auto value = getprop(prop_key[i]);
+ if (!value.empty() && value != prop_value[i])
+ setprop(prop_key[i], prop_value[i], false);
+ }
+}
+
+static inline void lazy_unmount(const char* mountpoint) {
+ if (umount2(mountpoint, MNT_DETACH) != -1)
+ LOGD("hide_policy: Unmounted (%s)\n", mountpoint);
+}
+
+void hide_daemon(int pid) {
+ RunFinally fin([=]() -> void {
+ // Send resume signal
+ tgkill(pid, pid, SIGCONT);
+ _exit(0);
+ });
+
+ if (switch_mnt_ns(pid))
+ return;
+
+ LOGD("hide_policy: handling PID=[%d]\n", pid);
+
+ char val;
+ int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
+ xxread(fd, &val, sizeof(val));
+ close(fd);
+ // Permissive
+ if (val == '0') {
+ chmod(SELINUX_ENFORCE, 0640);
+ chmod(SELINUX_POLICY, 0440);
+ }
+
+ getprop([](const char *name, auto, auto) -> void {
+ if (strstr(name, "magisk"))
+ deleteprop(name);
+ }, nullptr, false);
+
+ vector targets;
+
+ // Unmount dummy skeletons and /sbin links
+ file_readline("/proc/self/mounts", [&](string_view s) -> bool {
+ if (str_contains(s, "tmpfs /system/") || str_contains(s, "tmpfs /vendor/") ||
+ str_contains(s, "tmpfs /sbin")) {
+ char *path = (char *) s.data();
+ // Skip first token
+ strtok_r(nullptr, " ", &path);
+ targets.emplace_back(strtok_r(nullptr, " ", &path));
+ }
+ return true;
+ });
+
+ for (auto &s : targets)
+ lazy_unmount(s.data());
+ targets.clear();
+
+ // Unmount all Magisk created mounts
+ file_readline("/proc/self/mounts", [&](string_view s) -> bool {
+ if (str_contains(s, BLOCKDIR)) {
+ char *path = (char *) s.data();
+ // Skip first token
+ strtok_r(nullptr, " ", &path);
+ targets.emplace_back(strtok_r(nullptr, " ", &path));
+ }
+ return true;
+ });
+
+ for (auto &s : targets)
+ lazy_unmount(s.data());
+}
+
diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp
index b95c40953..3489660d2 100644
--- a/native/jni/magiskhide/hide_utils.cpp
+++ b/native/jni/magiskhide/hide_utils.cpp
@@ -1,6 +1,5 @@
#include
#include
-#include
#include
#include
#include
@@ -11,9 +10,7 @@
#include
#include
-#include
#include
-#include
#include "magiskhide.h"
@@ -21,39 +18,6 @@ using namespace std;
static pthread_t proc_monitor_thread;
-static const char *prop_key[] =
- { "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked",
- "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit", "ro.debuggable",
- "ro.secure", "ro.build.type", "ro.build.tags", "ro.build.selinux", nullptr };
-
-static const char *prop_value[] =
- { "locked", "green", "1",
- "enforcing", "0", "0", "0",
- "1", "user", "release-keys", "0", nullptr };
-
-void manage_selinux() {
- char val;
- int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
- xxread(fd, &val, sizeof(val));
- close(fd);
- // Permissive
- if (val == '0') {
- chmod(SELINUX_ENFORCE, 0640);
- chmod(SELINUX_POLICY, 0440);
- }
-}
-
-static void hide_sensitive_props() {
- LOGI("hide_utils: Hiding sensitive props\n");
-
- // Hide all sensitive props
- for (int i = 0; prop_key[i]; ++i) {
- auto value = getprop(prop_key[i]);
- if (!value.empty() && value != prop_value[i])
- setprop(prop_key[i], prop_value[i], false);
- }
-}
-
// Leave /proc fd opened as we're going to read from it repeatedly
static DIR *procfp;
void crawl_procfs(const function &fn) {
@@ -117,13 +81,6 @@ static void kill_process(const char *name) {
});
}
-void clean_magisk_props() {
- getprop([](const char *name, auto, auto) -> void {
- if (strstr(name, "magisk"))
- deleteprop(name);
- }, nullptr, false);
-}
-
static int add_list(const char *pkg, const char *proc = "") {
if (proc[0] == '\0')
proc = pkg;
@@ -322,9 +279,13 @@ void auto_start_magiskhide() {
db_settings dbs;
get_db_settings(dbs, HIDE_CONFIG);
if (dbs[HIDE_CONFIG]) {
- new_daemon_thread([](auto) -> void* {
- launch_magiskhide(-1);
- return nullptr;
- });
+ new_daemon_thread([]() -> void { launch_magiskhide(-1); });
}
}
+
+void test_proc_monitor() {
+ if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
+ exit(1);
+ proc_monitor();
+ exit(0);
+}
diff --git a/native/jni/magiskhide/magiskhide.cpp b/native/jni/magiskhide/magiskhide.cpp
index 12fae8039..688e5c0c0 100644
--- a/native/jni/magiskhide/magiskhide.cpp
+++ b/native/jni/magiskhide/magiskhide.cpp
@@ -13,19 +13,24 @@
#include "magiskhide.h"
+using namespace std::literals;
+
bool hide_enabled = false;
[[noreturn]] static void usage(char *arg0) {
fprintf(stderr,
FULL_VER(MagiskHide) "\n\n"
- "Usage: %s [--option [arguments...] ]\n\n"
- "Options:\n"
- " --status Return the status of magiskhide\n"
- " --enable Start magiskhide\n"
- " --disable Stop magiskhide\n"
- " --add PKG [PROC] Add a new target to the hide list\n"
- " --rm PKG [PROC] Remove from the hide list\n"
- " --ls List the current hide list\n"
+ "Usage: %s [action [arguments...] ]\n\n"
+ "Actions:\n"
+ " status Return the status of magiskhide\n"
+ " enable Start magiskhide\n"
+ " disable Stop magiskhide\n"
+ " add PKG [PROC] Add a new target to the hide list\n"
+ " rm PKG [PROC] Remove target(s) from the hide list\n"
+ " ls Print the current hide list\n"
+#ifdef MAGISK_DEBUG
+ " test Run process monitor test\n"
+#endif
, arg0);
exit(1);
}
@@ -76,19 +81,28 @@ int magiskhide_main(int argc, char *argv[]) {
if (argc < 2)
usage(argv[0]);
+ // CLI backwards compatibility
+ const char *opt = argv[1];
+ if (opt[0] == '-' && opt[1] == '-')
+ opt += 2;
+
int req;
- if (strcmp(argv[1], "--enable") == 0)
+ if (opt == "enable"sv)
req = LAUNCH_MAGISKHIDE;
- else if (strcmp(argv[1], "--disable") == 0)
+ else if (opt == "disable"sv)
req = STOP_MAGISKHIDE;
- else if (strcmp(argv[1], "--add") == 0 && argc > 2)
+ else if (opt == "add"sv)
req = ADD_HIDELIST;
- else if (strcmp(argv[1], "--rm") == 0 && argc > 2)
+ else if (opt == "rm"sv)
req = RM_HIDELIST;
- else if (strcmp(argv[1], "--ls") == 0)
+ else if (opt == "ls"sv)
req = LS_HIDELIST;
- else if (strcmp(argv[1], "--status") == 0)
+ else if (opt == "status"sv)
req = HIDE_STATUS;
+#ifdef MAGISK_DEBUG
+ else if (opt == "test"sv)
+ test_proc_monitor();
+#endif
else
usage(argv[0]);
diff --git a/native/jni/magiskhide/magiskhide.h b/native/jni/magiskhide/magiskhide.h
index e60ba8a51..05d0f65ea 100644
--- a/native/jni/magiskhide/magiskhide.h
+++ b/native/jni/magiskhide/magiskhide.h
@@ -25,17 +25,20 @@ int stop_magiskhide();
int add_list(int client);
int rm_list(int client);
void ls_list(int client);
+[[noreturn]] void test_proc_monitor();
// Process monitoring
void proc_monitor();
void update_uid_map();
// Utility functions
-void manage_selinux();
-void clean_magisk_props();
void crawl_procfs(const std::function &fn);
void crawl_procfs(DIR *dir, const std::function &fn);
+// Hide policies
+void hide_daemon(int pid);
+void hide_sensitive_props();
+
extern bool hide_enabled;
extern pthread_mutex_t monitor_lock;
extern std::set> hide_set;
diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp
index 0881112c8..f68f93960 100644
--- a/native/jni/magiskhide/proc_monitor.cpp
+++ b/native/jni/magiskhide/proc_monitor.cpp
@@ -1,12 +1,3 @@
-/* proc_monitor.cpp - Monitor am_proc_start events and unmount
- *
- * We monitor the listed APK files from /data/app until they get opened
- * via inotify to detect a new app launch.
- *
- * If it's a target we pause it ASAP, and fork a new process to join
- * its mount namespace and do all the unmounting/mocking.
- */
-
#include
#include
#include
@@ -58,11 +49,6 @@ static inline int read_ns(const int pid, struct stat *st) {
return stat(path, st);
}
-static inline void lazy_unmount(const char* mountpoint) {
- if (umount2(mountpoint, MNT_DETACH) != -1)
- LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
-}
-
static int parse_ppid(int pid) {
char path[32];
int ppid;
@@ -79,19 +65,15 @@ static int parse_ppid(int pid) {
return ppid;
}
-static long xptrace(bool log, int request, pid_t pid, void *addr, void *data) {
+static inline long xptrace(int request, pid_t pid, void *addr, void *data) {
long ret = ptrace(request, pid, addr, data);
- if (log && ret == -1)
+ if (ret < 0)
PLOGE("ptrace %d", pid);
return ret;
}
-static long xptrace(int request, pid_t pid, void *addr, void *data) {
- return xptrace(true, request, pid, addr, data);
-}
-
-static long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) {
- return xptrace(true, request, pid, addr, reinterpret_cast(data));
+static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) {
+ return xptrace(request, pid, addr, reinterpret_cast(data));
}
static bool parse_packages_xml(string_view s) {
@@ -141,25 +123,17 @@ void update_uid_map() {
}
static void check_zygote() {
- int min_zyg = 1;
- if (access("/system/bin/app_process64", R_OK) == 0)
- min_zyg = 2;
- for (;;) {
- crawl_procfs([](int pid) -> bool {
- char buf[512];
- snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
- if (FILE *f = fopen(buf, "re"); f) {
- fgets(buf, sizeof(buf), f);
- if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1)
- new_zygote(pid);
- fclose(f);
- }
- return true;
- });
- if (zygote_map.size() >= min_zyg)
- break;
- usleep(10000);
- }
+ crawl_procfs([](int pid) -> bool {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+ if (FILE *f = fopen(buf, "re"); f) {
+ fgets(buf, sizeof(buf), f);
+ if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1)
+ new_zygote(pid);
+ fclose(f);
+ }
+ return true;
+ });
}
#define APP_PROC "/system/bin/app_process"
@@ -172,8 +146,8 @@ static void setup_inotify() {
// Setup inotify asynchronous I/O
fcntl(inotify_fd, F_SETFL, O_ASYNC);
struct f_owner_ex ex = {
- .type = F_OWNER_TID,
- .pid = gettid()
+ .type = F_OWNER_TID,
+ .pid = gettid()
};
fcntl(inotify_fd, F_SETOWN_EX, &ex);
@@ -188,60 +162,6 @@ static void setup_inotify() {
} else {
inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS);
}
-
- // First find existing zygotes
- check_zygote();
-}
-
-/*************************
- * The actual hide daemon
- *************************/
-
-static void hide_daemon(int pid) {
- RunFinally fin([=]() -> void {
- // Send resume signal
- tgkill(pid, pid, SIGCONT);
- _exit(0);
- });
-
- if (switch_mnt_ns(pid))
- return;
-
- LOGD("hide_daemon: handling PID=[%d]\n", pid);
- manage_selinux();
- clean_magisk_props();
-
- vector targets;
-
- // Unmount dummy skeletons and /sbin links
- file_readline("/proc/self/mounts", [&](string_view s) -> bool {
- if (str_contains(s, "tmpfs /system/") || str_contains(s, "tmpfs /vendor/") ||
- str_contains(s, "tmpfs /sbin")) {
- char *path = (char *) s.data();
- // Skip first token
- strtok_r(nullptr, " ", &path);
- targets.emplace_back(strtok_r(nullptr, " ", &path));
- }
- return true;
- });
-
- for (auto &s : targets)
- lazy_unmount(s.data());
- targets.clear();
-
- // Unmount all Magisk created mounts
- file_readline("/proc/self/mounts", [&](string_view s) -> bool {
- if (str_contains(s, BLOCKDIR)) {
- char *path = (char *) s.data();
- // Skip first token
- strtok_r(nullptr, " ", &path);
- targets.emplace_back(strtok_r(nullptr, " ", &path));
- }
- return true;
- });
-
- for (auto &s : targets)
- lazy_unmount(s.data());
}
/************************
@@ -261,13 +181,9 @@ static void inotify_event(int) {
char buf[512];
auto event = reinterpret_cast(buf);
read(inotify_fd, buf, sizeof(buf));
- if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) {
- LOGD("proc_monitor: /data/system/packages.xml updated\n");
- uid_proc_map.clear();
- file_readline("/data/system/packages.xml", parse_packages_xml, true);
- } else if (event->mask & IN_ACCESS) {
- check_zygote();
- }
+ if ((event->mask & IN_CLOSE_WRITE) && event->name == "packages.xml"sv)
+ update_uid_map();
+ check_zygote();
}
// Workaround for the lack of pthread_cancel
@@ -291,7 +207,7 @@ static void term_thread(int) {
* Ptrace Madness
******************/
-/* Ptrace is super tricky, preserve all excessive debug in code
+/* Ptrace is super tricky, preserve all excessive logging in code
* but disable when actually building for usage (you won't want
* your logcat spammed with new thread events from all apps) */
@@ -300,6 +216,7 @@ static void term_thread(int) {
static void detach_pid(int pid, int signal = 0) {
char path[128];
+ attaches[pid] = false;
xptrace(PTRACE_DETACH, pid, nullptr, signal);
// Detach all child threads too
@@ -333,10 +250,6 @@ static bool check_pid(int pid) {
if (strncmp(cmdline, "zygote", 6) == 0)
return false;
- /* This process is fully initialized, we will stop
- * tracing it no matter if it is a target or not. */
- attaches[pid] = false;
-
sprintf(path, "/proc/%d", pid);
struct stat st;
lstat(path, &st);
@@ -371,20 +284,24 @@ static bool check_pid(int pid) {
}
}
}
- PTRACE_LOG("not our target\n");
+ PTRACE_LOG("[%s] not our target\n", cmdline);
detach_pid(pid);
return true;
}
static void new_zygote(int pid) {
- if (zygote_map.count(pid))
- return;
-
- LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid);
-
struct stat st;
if (read_ns(pid, &st))
return;
+
+ auto it = zygote_map.find(pid);
+ if (it != zygote_map.end()) {
+ // Update namespace info
+ it->second = st;
+ return;
+ }
+
+ LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid);
zygote_map[pid] = st;
xptrace(PTRACE_ATTACH, pid);
@@ -414,12 +331,26 @@ void proc_monitor() {
setup_inotify();
+ // First find existing zygotes
+ check_zygote();
+
int status;
for (;;) {
const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
- if (pid < 0)
+ if (pid < 0) {
+ if (errno == ECHILD) {
+ /* This mean we have nothing to wait, sleep
+ * and wait till signal interruption */
+ LOGD("proc_monitor: nothing to monitor, wait for signal\n");
+ struct timespec ts = {
+ .tv_sec = INT_MAX,
+ .tv_nsec = 0
+ };
+ nanosleep(&ts, nullptr);
+ }
continue;
+ }
bool detach = false;
RunFinally detach_task([&]() -> void {
if (detach) {
@@ -427,12 +358,13 @@ void proc_monitor() {
attaches[pid] = false;
detaches[pid] = false;
ptrace(PTRACE_DETACH, pid, 0, 0);
+ PTRACE_LOG("detach\n");
}
});
- if (!WIFSTOPPED(status) || detaches[pid]) {
- PTRACE_LOG("detached\n");
+
+ if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */ || detaches[pid])
DETACH_AND_CONT;
- }
+
if (WSTOPSIG(status) == SIGTRAP && WEVENT(status)) {
unsigned long msg;
xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
@@ -446,13 +378,11 @@ void proc_monitor() {
break;
case PTRACE_EVENT_EXIT:
PTRACE_LOG("zygote exited with status: [%d]\n", msg);
+ [[fallthrough]];
+ default:
zygote_map.erase(pid);
DETACH_AND_CONT;
- default:
- PTRACE_LOG("unknown event: %d\n", WEVENT(status));
- break;
}
- xptrace(PTRACE_CONT, pid);
} else {
switch (WEVENT(status)) {
case PTRACE_EVENT_CLONE:
@@ -462,14 +392,13 @@ void proc_monitor() {
break;
case PTRACE_EVENT_EXEC:
case PTRACE_EVENT_EXIT:
- PTRACE_LOG("exited or execve\n");
- DETACH_AND_CONT;
+ PTRACE_LOG("exit or execve\n");
+ [[fallthrough]];
default:
- PTRACE_LOG("unknown event: %d\n", WEVENT(status));
- break;
+ DETACH_AND_CONT;
}
- xptrace(PTRACE_CONT, pid);
}
+ xptrace(PTRACE_CONT, pid);
} else if (WSTOPSIG(status) == SIGSTOP) {
PTRACE_LOG("SIGSTOP from child\n");
xptrace(PTRACE_SETOPTIONS, pid, nullptr,
diff --git a/native/jni/utils/file.cpp b/native/jni/utils/file.cpp
index 6329bd89e..9f92d0bde 100644
--- a/native/jni/utils/file.cpp
+++ b/native/jni/utils/file.cpp
@@ -331,10 +331,6 @@ void *__mmap(const char *filename, size_t *size, bool rw) {
return buf;
}
-void mmap_ro(const char *filename, void **buf, size_t *size) {
- *buf = __mmap(filename, size, false);
-}
-
void fd_full_read(int fd, void **buf, size_t *size) {
*size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
diff --git a/native/jni/utils/include/utils.h b/native/jni/utils/include/utils.h
index 12292d37f..8574eb054 100644
--- a/native/jni/utils/include/utils.h
+++ b/native/jni/utils/include/utils.h
@@ -118,7 +118,6 @@ int setattrat(int dirfd, const char *name, struct file_attr *a);
int fsetattr(int fd, struct file_attr *a);
void fclone_attr(int sourcefd, int targetfd);
void clone_attr(const char *source, const char *target);
-void mmap_ro(const char *filename, void **buf, size_t *size);
void fd_full_read(int fd, void **buf, size_t *size);
void full_read(const char *filename, void **buf, size_t *size);
void write_zero(int fd, size_t size);
@@ -208,6 +207,7 @@ int parse_int(S __s) { return parse_int(__s.data()); }
int new_daemon_thread(void *(*start_routine) (void *), void *arg = nullptr,
const pthread_attr_t *attr = nullptr);
+int new_daemon_thread(std::function &&fn);
struct exec_t {
bool err = false;
diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp
index b82a06006..4a8ba0b53 100644
--- a/native/jni/utils/misc.cpp
+++ b/native/jni/utils/misc.cpp
@@ -200,6 +200,17 @@ int new_daemon_thread(void *(*start_routine) (void *), void *arg, const pthread_
return ret;
}
+static void *proxy_routine(void *fp) {
+ auto fn = reinterpret_cast*>(fp);
+ (*fn)();
+ delete fn;
+ return nullptr;
+}
+
+int new_daemon_thread(std::function &&fn) {
+ return new_daemon_thread(proxy_routine, new std::function(std::move(fn)));
+}
+
static char *argv0;
static size_t name_len;
void init_argv0(int argc, char **argv) {