From 4771c2810baf4f3910d9fa0cb94551ab5c21cdef Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 26 Aug 2021 03:09:56 -0700 Subject: [PATCH] Significantly better AVD support --- native/jni/core/bootstages.cpp | 6 +- native/jni/core/daemon.cpp | 6 ++ native/jni/core/magisk.cpp | 5 ++ native/jni/include/daemon.hpp | 3 +- native/jni/magiskhide/hide_policy.cpp | 11 +-- native/jni/magiskhide/magiskhide.cpp | 5 ++ native/jni/utils/misc.hpp | 2 +- scripts/emulator.sh | 120 ++++++++++++++++---------- 8 files changed, 103 insertions(+), 55 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index d0a578d74..0251ad70e 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -23,9 +23,9 @@ static bool safe_mode = false; * Setup * *********/ -#define MNT_DIR_IS(dir) (me->mnt_dir == string_view(dir)) -#define SETMIR(b, part) sprintf(b, "%s/" MIRRDIR "/" #part, MAGISKTMP.data()) -#define SETBLK(b, part) sprintf(b, "%s/" BLOCKDIR "/" #part, MAGISKTMP.data()) +#define MNT_DIR_IS(dir) (me->mnt_dir == string_view(dir)) +#define SETMIR(b, part) snprintf(b, sizeof(b), "%s/" MIRRDIR "/" #part, MAGISKTMP.data()) +#define SETBLK(b, part) snprintf(b, sizeof(b), "%s/" BLOCKDIR "/" #part, MAGISKTMP.data()) #define do_mount_mirror(part, flag) {\ SETMIR(buf1, part); \ diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index e8b7846a3..e9e5f1a46 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -91,6 +91,11 @@ static void handle_request_sync(int client, int code) { case START_DAEMON: setup_logfile(true); break; + case STOP_DAEMON: + magiskhide_handler(-1, nullptr); + write_int(client, 0); + // Terminate the daemon! + exit(0); } } @@ -120,6 +125,7 @@ static void handle_request(int client) { case SQLITE_CMD: case GET_PATH: case MAGISKHIDE: + case STOP_DAEMON: if (!is_root) { write_int(client, ROOT_REQUIRED); goto done; diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index 27c1ae31d..b2616aa23 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -28,6 +28,7 @@ Options: Advanced Options (Internal APIs): --daemon manually start magisk daemon + --stop remove all magisk changes and stop daemon --[init trigger] start service for init trigger Supported init triggers: post-fs-data, service, boot-complete @@ -84,6 +85,10 @@ int magisk_main(int argc, char *argv[]) { int fd = connect_daemon(true); write_int(fd, START_DAEMON); return 0; + } else if (argv[1] == "--stop"sv) { + int fd = connect_daemon(); + write_int(fd, STOP_DAEMON); + return read_int(fd); } else if (argv[1] == "--post-fs-data"sv) { int fd = connect_daemon(true); write_int(fd, POST_FS_DATA); diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index a5ff833b8..10971e92d 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -19,7 +19,8 @@ enum : int { CHECK_VERSION = SYNC_FLAG | 1, CHECK_VERSION_CODE = SYNC_FLAG | 2, GET_PATH = SYNC_FLAG | 3, - SUPERUSER = 4, + STOP_DAEMON = SYNC_FLAG | 4, + SUPERUSER = 5, POST_FS_DATA, LATE_START, BOOT_COMPLETE, diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp index 19d316fd8..ffd0573a4 100644 --- a/native/jni/magiskhide/hide_policy.cpp +++ b/native/jni/magiskhide/hide_policy.cpp @@ -25,14 +25,15 @@ void hide_daemon(int pid, int client) { #define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && str_starts(mentry->mnt_dir, "/" #dir)) void hide_unmount(int pid) { - if (pid > 0 && switch_mnt_ns(pid)) - return; - - LOGD("hide: handling PID=[%d]\n", pid > 0 ? pid : getpid()); + if (pid > 0) { + if (switch_mnt_ns(pid)) + return; + LOGD("hide: handling PID=[%d]\n", pid); + } vector targets; - // Unmount dummy skeletons and /sbin links + // Unmount dummy skeletons and MAGISKTMP targets.push_back(MAGISKTMP); parse_mnt("/proc/self/mounts", [&](mntent *mentry) { if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(product) || TMPFS_MNT(system_ext)) diff --git a/native/jni/magiskhide/magiskhide.cpp b/native/jni/magiskhide/magiskhide.cpp index e095163f4..53daa4b8a 100644 --- a/native/jni/magiskhide/magiskhide.cpp +++ b/native/jni/magiskhide/magiskhide.cpp @@ -25,6 +25,11 @@ using namespace std; } void magiskhide_handler(int client, ucred *cred) { + if (client < 0) { + hide_unmount(); + return; + } + int req = read_int(client); int res = DAEMON_ERROR; diff --git a/native/jni/utils/misc.hpp b/native/jni/utils/misc.hpp index e87046d8d..cda1f75f2 100644 --- a/native/jni/utils/misc.hpp +++ b/native/jni/utils/misc.hpp @@ -126,7 +126,7 @@ int exec_command_sync(exec_t &exec, Args &&...args) { } template int exec_command_sync(Args &&...args) { - exec_t exec{}; + exec_t exec; return exec_command_sync(exec, args...); } template diff --git a/scripts/emulator.sh b/scripts/emulator.sh index d88b75871..b2569757a 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -3,16 +3,19 @@ # AVD Magisk Setup ##################################################################### # -# This script will setup an environment with minimal Magisk that -# the Magisk app will be happy to run properly within the official -# emulator bundled with Android Studio (AVD). +# Support emulator ABI: x86_64 *only* +# Support API level: 23 - 31 (21 and 22 images do not have SELinux) # -# ONLY use this script for developing the Magisk app or root apps -# in the emulator. The constructed Magisk environment is not a -# fully functional one as if it is running on an actual device. +# This script will stop zygote, simulate the Magisk start up process +# that would've happened before zygote was started, and finally +# restart zygote. This is useful for setting up the emulator for +# developing Magisk, testing modules, and developing root apps using +# the official Android emulator (AVD) instead of a real device. # -# The script assumes you are using x64 emulator images. -# Build binaries with `./build.py binary` before running this script. +# This only covers the "core" features of Magisk. Testing magiskinit +# and magiskboot require additional setups that are not covered here. +# +# Build everything by `./build.py all` before running this script. # ##################################################################### @@ -29,16 +32,13 @@ mount_sbin() { if [ ! -f /system/build.prop ]; then # Running on PC cd "$(dirname "$0")/.." - adb push native/out/x86_64/busybox native/out/x86_64/magiskinit \ - native/out/x86_64/magisk scripts/emulator.sh /data/local/tmp + adb push native/out/x86_64/busybox out/app-debug.apk scripts/emulator.sh /data/local/tmp adb shell sh /data/local/tmp/emulator.sh exit 0 fi cd /data/local/tmp -chmod 777 busybox -chmod 777 magiskinit -chmod 777 magisk +chmod 755 busybox if [ -z "$FIRST_STAGE" ]; then export FIRST_STAGE=1 @@ -52,20 +52,34 @@ if [ -z "$FIRST_STAGE" ]; then fi fi -# Remove previous setup if exist -pgrep magiskd >/dev/null && pkill -9 magiskd -[ -f /sbin/magisk ] && umount -l /sbin -[ -f /system/bin/magisk ] && umount -l /system/bin -if [ -d /dev/magisk ]; then - umount -l /dev/magisk 2>/dev/null - rm -rf /dev/magisk +pm install -r $(pwd)/app-debug.apk + +# Extract files from APK +unzip -oj app-debug.apk 'lib/x86_64/*' 'lib/x86/libmagisk32.so' -x 'lib/x86_64/busybox.so' +for file in lib*.so; do + chmod 755 $file + mv "$file" "${file:3:${#file}-6}" +done + +# Stop zygote (and previous setup if exists) +magisk --stop 2>/dev/null +stop +if [ -d /dev/avd-magisk ]; then + umount -l /dev/avd-magisk 2>/dev/null + rm -rf /dev/avd-magisk 2>/dev/null fi # SELinux stuffs ln -sf ./magiskinit magiskpolicy -./magiskpolicy --live --magisk +if [ -f /vendor/etc/selinux/precompiled_sepolicy ]; then + ./magiskpolicy --load /vendor/etc/selinux/precompiled_sepolicy --live --magisk +elif [ -f /sepolicy ]; then + ./magiskpolicy --load /sepolicy --live --magisk +else + ./magiskpolicy --live --magisk +fi -BINDIR=/sbin +MAGISKTMP=/sbin # Setup bin overlay if mount | grep -q rootfs; then @@ -81,13 +95,11 @@ if mount | grep -q rootfs; then elif [ -e /sbin ]; then # Legacy SAR mount_sbin - if ! grep -q '/sbin/.magisk/mirror/system_root' /proc/mounts; then - mkdir -p /sbin/.magisk/mirror/system_root - block=$(mount | grep ' / ' | awk '{ print $1 }') - [ $block = "/dev/root" ] && block=/dev/block/dm-0 - mount -o ro $block /sbin/.magisk/mirror/system_root - fi - for file in /sbin/.magisk/mirror/system_root/sbin/*; do + mkdir -p /dev/sysroot + block=$(mount | grep ' / ' | awk '{ print $1 }') + [ $block = "/dev/root" ] && block=/dev/block/dm-0 + mount -o ro $block /dev/sysroot + for file in /dev/sysroot/sbin/*; do [ ! -e $file ] && break if [ -L $file ]; then cp -af $file /sbin @@ -97,25 +109,43 @@ elif [ -e /sbin ]; then mount -o bind $file $sfile fi done + umount -l /dev/sysroot + rm -rf /dev/sysroot else - # Android Q+ without sbin, use overlayfs - BINDIR=/dev/magisk/upper - mkdir /dev/magisk - mount -t tmpfs -o 'mode=0755' tmpfs /dev/magisk - chcon u:object_r:system_file:s0 /dev/magisk - mkdir /dev/magisk/upper - mkdir /dev/magisk/work - ./magisk --clone-attr /system/bin /dev/magisk/upper - mount -t overlay overlay -o lowerdir=/system/bin,upperdir=/dev/magisk/upper,workdir=/dev/magisk/work /system/bin + # Android Q+ without sbin + MAGISKTMP=/dev/avd-magisk + mkdir /dev/avd-magisk + mount -t tmpfs -o 'mode=0755' tmpfs /dev/avd-magisk fi # Magisk stuffs -cp -af ./magisk $BINDIR/magisk -chmod 755 $BINDIR/magisk -ln -s ./magisk $BINDIR/su -ln -s ./magisk $BINDIR/resetprop -ln -s ./magisk $BINDIR/magiskhide -mkdir -p /data/adb/modules 2>/dev/null +mkdir -p /data/adb/magisk 2>/dev/null +unzip -oj app-debug.apk 'assets/*' -x 'assets/chromeos/*' -d /data/adb/magisk +mkdir /data/adb/modules 2>/dev/null mkdir /data/adb/post-fs-data.d 2>/dev/null mkdir /data/adb/services.d 2>/dev/null -$BINDIR/magisk --daemon + +for file in magisk32 magisk64 magiskinit; do + chmod 755 ./$file + cp -af ./$file $MAGISKTMP/$file + cp -af ./$file /data/adb/magisk/$file +done +cp -af ./magiskboot /data/adb/magisk/magiskboot +cp -af ./busybox /data/adb/magisk/busybox + +ln -s ./magisk64 $MAGISKTMP/magisk +ln -s ./magisk $MAGISKTMP/su +ln -s ./magisk $MAGISKTMP/resetprop +ln -s ./magisk $MAGISKTMP/magiskhide +ln -s ./magiskinit $MAGISKTMP/magiskpolicy + +mkdir -p $MAGISKTMP/.magisk/mirror +mkdir $MAGISKTMP/.magisk/block +touch $MAGISKTMP/.magisk/config + +# Boot up +$MAGISKTMP/magisk --post-fs-data +while [ ! -f /dev/.magisk_unblock ]; do sleep 1; done +rm /dev/.magisk_unblock +start +$MAGISKTMP/magisk --service