首页 > 其他分享 >Magisk移植

Magisk移植

时间:2023-02-20 12:22:05浏览次数:25  
标签:sbin Magisk root init 移植 data magisk mode

Magisk

启动流程

rootfs 补丁

#######################################################################################
# Magisk Boot Image Patcher
#######################################################################################
#
# Usage: boot_patch.sh <bootimage>
#
# The following flags can be set in environment variables:
# KEEPVERITY, KEEPFORCEENCRYPT, PATCHVBMETAFLAG, RECOVERYMODE
#
# This script should be placed in a directory with the following files:
#
# File name          Type      Description
#
# boot_patch.sh      script    A script to patch boot image for Magisk.
#                  (this file) The script will use files in its same
#                              directory to complete the patching process.
# util_functions.sh  script    A script which hosts all functions required
#                              for this script to work properly.
# magiskinit         binary    The binary to replace /init.
# magisk(32/64)      binary    The magisk binaries.
# magiskboot         binary    A tool to manipulate boot images.
# stub.apk           binary    The stub Magisk app to embed into ramdisk.
# chromeos           folder    This folder includes the utility and keys to sign
#                  (optional)  chromeos boot images. Only used for Pixel C.
#
#######################################################################################

主要是替换一些关键二进制程序,比如magiskinit 替换 init

rootfs init 流程

class RootFSInit : public MagiskInit {
private:
    void prepare();
public:
    RootFSInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
        LOGD("%s\n", __FUNCTION__);
    }
    void start() override {
        prepare();
        patch_rw_root();
        exec_init();
    }
};

prepare()

创建/data,挂载tmpfs,将roofs中部分文件拷贝/data中

void RootFSInit::prepare()
{
    prepare_data();
    LOGD("Restoring /init\n");
    rename(backup_init(), "/init"); //还原真正的init 待会还要调用的
}
// 创建/data,挂载tmpfs,将roofs中部分文件拷贝/data中
void BaseInit::prepare_data() {
    LOGD("Setup data tmp\n");
    xmkdir("/data", 0755);
    xmount("tmpfs", "/data", "tmpfs", 0, "mode=755");

    cp_afc("/init", "/data/magiskinit");
    cp_afc("/.backup", "/data/.backup");
    cp_afc("/overlay.d", "/data/overlay.d");
}

patch_rw_root()

void MagiskInit::patch_rw_root()
{
    mount_list.emplace_back("/data");  //添加到mount_list,等下要umount的
    mkdir("/root", 0777);
    clone_attr("/sbin", "/root");
    link_path("/sbin", "/root");

    // 给init.rc 打补丁,添加magisk的开机启动
    patch_init_rc("/init.rc", "/init.p.rc", "/sbin");
    rename("/init.p.rc", "/init.rc");
    ...
    // 创建/magisk-tmp
    // 找到分区块,在/data/.magisk/block下mknod, 然后挂载到/data/.magisk/mirror
    // 将/data bind 挂载到/magisk-tmp , /sbin挂载为tmpfs ,将/magisk-tmp再bind /sbin 套娃
    xmkdir(PRE_TMPDIR, 0);
    setup_tmp(PRE_TMPDIR);
    chdir(PRE_TMPDIR);
    ...
    chdir("/");

    // Dump magiskinit as magisk
    cp_afc(REDIR_PATH, "/sbin/magisk");
}

exec_init()

void BaseInit::exec_init() {
    // Unmount in reverse order
    //取消之前的挂载点
    for (auto &p : reversed(mount_list)) {
         if (xumount2(p.data(), MNT_DETACH) == 0)
             LOGD("Unmount [%s]\n", p.data());
    }
    execv("/init", argv);
    exit(1);
}

init.rc中添加的patch,增加了magisk的启动服务

on post-fs-data
    start logd
    rm /dev/.magisk_unblock
    start AtAamQHB
    wait /dev/.magisk_unblock 40
    rm /dev/.magisk_unblock

service AtAamQHB /sbin/magisk --post-fs-data
    user root
    seclabel u:r:magisk:s0
    oneshot

service pqvfozQFFg1R /sbin/magisk --service
    class late_start
    user root
    seclabel u:r:magisk:s0
    oneshot

on property:sys.boot_completed=1
    exec /sbin/magisk --boot-complete

on property:init.svc.zygote=restarting
    exec /sbin/magisk --zygote-restart

on property:init.svc.zygote=stopped
    exec /sbin/magisk --zygote-restart

post-fs-data: decrypted (if necessary) and mounted
late_start: Does not start until after /data has been decrypted and mounted

post-fs-data 在前 late_start 在后,创建socket,通过poll机制绑定,后续的su resetprop 等操作都是通过sokcet传递给magiskd

true 参数会创建daemon
close(connect_daemon(MainRequest::POST_FS_DATA, true));
close(connect_daemon(MainRequest::LATE_START, true));

最后会经过 handle_request(pollfd *pfd)函数再调用到boot_stage_handler(int code)

void boot_stage_handler(int code) {
    // Make sure boot stage execution is always serialized
    static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
    mutex_guard lock(stage_lock);

    switch (code) {
    case MainRequest::POST_FS_DATA:
        if ((boot_state & FLAG_POST_FS_DATA_DONE) == 0)
            post_fs_data();
        close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
        break;
    case MainRequest::LATE_START:
        if ((boot_state & FLAG_POST_FS_DATA_DONE) && (boot_state & FLAG_SAFE_MODE) == 0)
            late_start();
        break;
    case MainRequest::BOOT_COMPLETE:
        if ((boot_state & FLAG_SAFE_MODE) == 0)
            boot_complete();
        break;
    default:
        __builtin_unreachable();
    }
}
static void post_fs_data() {
    if (getenv("REMOUNT_ROOT"))
        xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
    ...
    unlock_blocks();
    mount_mirrors();
    prune_su_access();
    
    if (access(SECURE_DIR, F_OK) != 0) {
        if (SDK_INT < 24) {
            xmkdir(SECURE_DIR, 0700);
        } else {
            LOGE(SECURE_DIR " is not present, abort\n");
            goto early_abort;
        }
    }

    if (!magisk_env()) { // 这里/data/adb/magisk/busybox一定要有执行权限,不然就early_abort
        LOGE("* Magisk environment incomplete, abort\n");
        goto early_abort;
    }
    
    exec_common_scripts("post-fs-data");
    ...
    handle_modules();
early_abort:
    // We still do magic mount because root itself might need it
    magic_mount();
    boot_state |= FLAG_POST_FS_DATA_DONE;
}
  • unlock_blocks
    对/dev/block下的所有块进行ioctl(fd, BLKROSET, &OFF)

  • mount_mirrors 通过/proc/mounts读取信息, 进行mknod创建设备, 然后挂载到mirror

    /sbin/.magisk/block/product on /sbin/.magisk/mirror/product type ext4 (ro,seclabel,relatime,discard)
    /sbin/.magisk/block/vendor on /sbin/.magisk/mirror/vendor type ext4 (ro,seclabel,relatime,discard)
    
  • exec_common_scripts 执行/data/adb/${stage}.d/*.sh必须具有执行权限

  • exec_module_scripts 执行 /data/adb/modules/${module}/${stage}.sh

  • handle_modules
    处理/data/adb/modules_update下的模块,基本就是移动到/data/adb/module/下,module_list中添加module相关数据

  • magic_mount()
    load_prop_file处理模块的system.prop文件 ,利用的还是resetprop修改系统属性,处理模块中的system文件夹,bind mount 挂载文件,到达覆盖效果

void magic_mount() {
    ...
    auto root = make_unique<root_node>("");
    auto system = new root_node("system");
    root->insert(system);
    
    char buf[4096];
    LOGI("* Loading modules\n");
    for (const auto &m : *module_list) {
        const char *module = m.name.data();
        char *b = buf + sprintf(buf, "%s/" MODULEMNT "/%s/", MAGISKTMP.data(), module);

        // Read props
        strcpy(b, "system.prop");
        if (access(buf, F_OK) == 0) {
            LOGI("%s: loading [system.prop]\n", module);
            load_prop_file(buf, false);
        }
        ...
        LOGI("%s: loading mount files\n", module);
        b[-1] = '\0';
        int fd = xopen(buf, O_RDONLY | O_CLOEXEC);
        system->collect_files(module, fd); //收集模块文件夹中的信息
        close(fd);
    }
    ...
    if (!system->is_empty()) {
        // Handle special read-only partitions
        for (const char *part : { "/vendor", "/product", "/system_ext" }) {
            struct stat st{};
            if (lstat(part, &st) == 0 && S_ISDIR(st.st_mode)) {
                if (auto old = system->extract(part + 1)) {
                    auto new_node = new root_node(old);
                    root->insert(new_node);
                }
            }
        }
        root->prepare();
        root->mount();
    }
    ...
}
void module_node::mount() {
    string src = module_mnt + module + parent()->root()->prefix + node_path();
    if (exist())
        clone_attr(mirror_path().data(), src.data());
    if (isa<tmpfs_node>(parent()))
        create_and_mount("module", src);
    else if (is_dir() || is_reg())
        bind_mount("module", src.data(), node_path().data());
}

总结下来就干两件事, mknod 和 bind mount .干完上面的事,效果大概如下

/sbin/.magisk/block/product on /sbin/.magisk/mirror/product type ext4 (ro,seclabel,relatime,discard)
/sbin/.magisk/block/vendor on /sbin/.magisk/mirror/vendor type ext4 (ro,seclabel,relatime,discard)
/sbin/.magisk/block/data on /sbin/.magisk/mirror/data type f2fs (rw,lazytime,seclabel,relatime,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier)
/sbin/.magisk/block/system_root on /sbin/.magisk/mirror/system_root type ext4 (ro,seclabel,relatime,discard,errors=remount-ro)
/sbin/.magisk/block/data on /sbin/.magisk/modules type f2fs (rw,lazytime,seclabel,relatime,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier)
/sbin/.magisk/block/data on /system/etc/hosts type f2fs (rw,lazytime,seclabel,relatime,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier)

/sbin/.magisk/block/data块设备对应 252:4, 跟/dev/block/dm-4一样, 不过dm-4挂载点是/data.

# ls -al /sbin/.magisk/block/data
brw------- 1 root root 252,   4 1972-08-27 15:40 /sbin/.magisk/block/data
# ls -al /system/etc/hosts
-rw-r--r-- 1 root root 70 2023-02-16 17:52 /system/etc/hosts

# ls -al /dev/block/dm-4
brw------- 1 root root 252,   4 1972-08-27 15:40 /dev/block/dm-4

# mount |grep dm-4
/dev/block/dm-4 on /data type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier)

我好奇的是 他怎么可以用块设备 挂载到文件上? 后来终于找到答案了, 只是mount看上是 ,但其实通过mountinfo来看就不是了

# cat /proc/1/mountinfo
cmi:/ # cat /proc/1/mountinfo |grep /sbin/.magisk/block/data
119 45 252:4 / /sbin/.magisk/mirror/data rw,relatime shared:17 - f2fs /sbin/.magisk/block/data rw,lazytime,seclabel,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier
123 45 252:4 /adb/modules /sbin/.magisk/modules rw,relatime shared:17 - f2fs /sbin/.magisk/block/data rw,lazytime,seclabel,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier
125 29 252:4 /adb/modules/Test_MagiskModule/system/etc/hosts /system/etc/hosts rw,relatime shared:17 - f2fs /sbin/.magisk/block/data rw,lazytime,seclabel,background_gc=on,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,alloc_mode=default,fsync_mode=nobarrier

模块

模块目录结构

Developer Guides 详细参阅

模块加载流程

模块中的META-INF/com/google/android/update-binary调用/data/adb/magisk/util_functions.sh之后就可以util_functions中使用提供的函数了
比如: mount 分区 修改selinux等等.最后会模块解压在/data/adb/modules_update下,

open-font例子中,是直接调用util_functions中提供的install_module安装

#########################
# Load util_functions.sh
#########################

OUTFD=$2
ZIPFILE=$3

mount /data 2>/dev/null #这步失败, 我们设备中fstab 没有 /data

[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
. /data/adb/magisk/util_functions.sh
[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk

install_module

重启的时候会调用handle_modules()处理模块,此函数在前面提到的post_fs_data流程中调用

总结

实现

因为magisk在rootfs中做的操作,主要3点:

  1. 将/sbin作为一个挂载点 往/sbin中放入文件
  2. 查找分区的设备块号,然后mknod, 挂载mirror下对应的目录
  3. 补丁init.rc,加入自己的启动服务
  4. 开启启动时候触发post-fs-data启动magiskd, 创建socket, 挂载分区, 加载之前安装的模块,做bind mount

标签:sbin,Magisk,root,init,移植,data,magisk,mode
From: https://www.cnblogs.com/tangshunhui/p/17136867.html

相关文章