#include #include #include #include #include #include #include #include "init.hpp" using namespace std; vector mount_list; template using chars = integer_sequence; template static string extract_qutoed_str_until(chars, chars, string_view sv, size_t &begin, bool "ed) { string result; char match_array[] = {escapes..., breaks..., '"'}; string_view match(match_array, sizeof(match_array)); for (auto end = begin;; ++end) { end = sv.find_first_of(match, end); if (end == string_view::npos || ((sv[end] == breaks) || ...) || (!quoted && ((sv[end] == escapes) || ...))) { result.append(sv.substr(begin, end - begin)); begin = end; return result; } if (sv[end] == '"') { quoted = !quoted; result.append(sv.substr(begin, end - begin)); begin = end + 1; } } } //[pair_split][key][assign_split][assign][assign_split][value][pair_split] template static auto parse(chars, chars, chars, string_view sv) { vector> kv; char next_array[] = {pair_spilt...}; string_view next(next_array, sizeof(next_array)); char skip_array[] = {assign..., assign_split...}; string_view skip(skip_array, sizeof(skip_array)); bool quoted = false; for (size_t cur = 0u; cur < sv.size(); cur = sv.find_first_not_of(next, cur)) { auto key = extract_qutoed_str_until(chars{}, chars{}, sv, cur, quoted); cur = sv.find_first_not_of(skip, cur); if (((cur == string_view::npos) || ... || (sv[cur] == pair_spilt))) { kv.emplace_back(key, ""); continue; } auto value = extract_qutoed_str_until(chars{}, chars<>{}, sv, cur, quoted); kv.emplace_back(key, value); } return kv; } static auto parse_cmdline(string_view sv) { return parse(chars<' '>{}, chars<'='>{}, chars<>{}, sv); } static auto parse_bootconfig(string_view sv) { return parse(chars<'\n'>{}, chars<'='>{}, chars<' '>{}, sv); } #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 (xmknod(name, S_IFCHR | 0444, makedev(13, minor))) 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); else close(fd); } if (events.empty()) return false; run_finally fin([&] { for_each(events.begin(), events.end(), close); }); // Return true if volume up key is held 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; } static FILE *kmsg; static char kmsg_buf[4096]; static int vprintk(const char *fmt, va_list ap) { vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap); return fprintf(kmsg, "%s", kmsg_buf); } void setup_klog() { // Shut down first 3 fds int fd; if (access("/dev/null", W_OK) == 0) { fd = xopen("/dev/null", O_RDWR | O_CLOEXEC); } else { mknod("/null", S_IFCHR | 0666, makedev(1, 3)); fd = xopen("/null", O_RDWR | O_CLOEXEC); unlink("/null"); } xdup3(fd, STDIN_FILENO, O_CLOEXEC); xdup3(fd, STDOUT_FILENO, O_CLOEXEC); xdup3(fd, STDERR_FILENO, O_CLOEXEC); if (fd > STDERR_FILENO) close(fd); if (access("/dev/kmsg", W_OK) == 0) { fd = xopen("/dev/kmsg", O_WRONLY | O_CLOEXEC); } else { mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); unlink("/kmsg"); } kmsg = fdopen(fd, "w"); setbuf(kmsg, nullptr); log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; log_cb.ex = nop_ex; strcpy(kmsg_buf, "magiskinit: "); // Disable kmsg rate limiting if (FILE *rate = fopen("/proc/sys/kernel/printk_devkmsg", "w")) { fprintf(rate, "on\n"); fclose(rate); } } #define read_dt(name, key) \ sprintf(file_name, "%s/" name, cmd->dt_dir); \ if (access(file_name, R_OK) == 0){ \ string data = full_read(file_name); \ if (!data.empty()) { \ data.pop_back(); \ strcpy(cmd->key, data.data()); \ } \ } void load_kernel_info(cmdline *cmd) { // Get kernel data using procfs and sysfs xmkdir("/proc", 0755); xmount("proc", "/proc", "proc", 0, nullptr); xmkdir("/sys", 0755); xmount("sysfs", "/sys", "sysfs", 0, nullptr); mount_list.emplace_back("/proc"); mount_list.emplace_back("/sys"); // Log to kernel setup_klog(); for (const auto&[key, value] : parse_cmdline(full_read("/proc/cmdline"))) { if (key == "androidboot.slot_suffix") { strcpy(cmd->slot, value.data()); } else if (key == "androidboot.slot") { cmd->slot[0] = '_'; strcpy(cmd->slot + 1, value.data()); } else if (key == "skip_initramfs") { cmd->skip_initramfs = true; } else if (key == "androidboot.force_normal_boot") { cmd->force_normal_boot = value[0] == '1'; } else if (key == "rootwait") { cmd->rootwait = true; } else if (key == "androidboot.android_dt_dir") { strcpy(cmd->dt_dir, value.data()); } else if (key == "androidboot.hardware") { strcpy(cmd->hardware, value.data()); } else if (key == "androidboot.hardware.platform") { strcpy(cmd->hardware_plat, value.data()); } else if (key == "androidboot.fstab_suffix") { strcpy(cmd->fstab_suffix, value.data()); } } LOGD("Kernel cmdline info:\n"); LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs); LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot); LOGD("rootwait=[%d]\n", cmd->rootwait); LOGD("slot=[%s]\n", cmd->slot); LOGD("dt_dir=[%s]\n", cmd->dt_dir); LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix); LOGD("hardware=[%s]\n", cmd->hardware); LOGD("hardware.platform=[%s]\n", cmd->hardware_plat); for (const auto&[key, value] : parse_bootconfig(full_read("/proc/bootconfig"))) { if (key == "androidboot.slot_suffix") { strcpy(cmd->slot, value.data()); } else if (key == "androidboot.force_normal_boot") { cmd->force_normal_boot = value[0] == '1'; } else if (key == "androidboot.android_dt_dir") { strcpy(cmd->dt_dir, value.data()); } else if (key == "androidboot.hardware") { strcpy(cmd->hardware, value.data()); } else if (key == "androidboot.hardware.platform") { strcpy(cmd->hardware_plat, value.data()); } else if (key == "androidboot.fstab_suffix") { strcpy(cmd->fstab_suffix, value.data()); } } LOGD("Boot config info:\n"); LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot); LOGD("slot=[%s]\n", cmd->slot); LOGD("dt_dir=[%s]\n", cmd->dt_dir); LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix); LOGD("hardware=[%s]\n", cmd->hardware); LOGD("hardware.platform=[%s]\n", cmd->hardware_plat); parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool { if (key == "RECOVERYMODE" && value == "true") { LOGD("Running in recovery mode, waiting for key...\n"); cmd->skip_initramfs = !check_key_combo(); return false; } return true; }); if (cmd->dt_dir[0] == '\0') strcpy(cmd->dt_dir, DEFAULT_DT_DIR); char file_name[128]; read_dt("fstab_suffix", fstab_suffix) read_dt("hardware", hardware) read_dt("hardware.platform", hardware_plat) LOGD("Device tree info:\n"); LOGD("dt_dir=[%s]\n", cmd->dt_dir); LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix); LOGD("hardware=[%s]\n", cmd->hardware); LOGD("hardware.platform=[%s]\n", cmd->hardware_plat); } bool check_two_stage() { if (access("/apex", F_OK) == 0) return true; if (access("/system/bin/init", F_OK) == 0) return true; // If we still have no indication, parse the original init and see what's up auto init = mmap_data::ro("/.backup/init"); return init.contains("selinux_setup"); }