对 Magisk 源码做一个简单的分析,本人很菜,分析不到位的地方请各位大神指出来。本文以 Magisk v25.1 为例
Patch boot.img
当使用 Magisk root 的时候,一般操作都是提取内核文件进行修补。直接上代码分析
关于修补内核的代码在 com.topjohnwu.magisk.core.tasks 类
1 protected fun patchFile (file: Uri ) = extractFiles() && handleFile(file)
继续跟进,排除一些无关紧要的逻辑后,来到这里.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 private fun patchBoot () : Boolean { var isSigned = false if (!srcBoot.isCharacter) { try { srcBoot.newInputStream().use { if (SignBoot.verifySignature(it, null )) { isSigned = true console.add("- Boot image is signed with AVB 1.0" ) } } } catch (e: IOException) { console.add("! Unable to check signature" ) Timber.e(e) return false } } val newBoot = installDir.getChildFile("new-boot.img" ) if (!useRootDir) { newBoot.createNewFile() File(installDir, "stock_boot.img" ).createNewFile() } val cmds = arrayOf( "cd $installDir " , "KEEPFORCEENCRYPT=${Config.keepEnc} " + "KEEPVERITY=${Config.keepVerity} " + "PATCHVBMETAFLAG=${Config.patchVbmeta} " + "RECOVERYMODE=${Config.recovery} " + "sh boot_patch.sh $srcBoot " ) if (!cmds.sh().isSuccess) return false val job = shell.newJob().add("./magiskboot cleanup" , "cd /" ) if (isSigned) { console.add("- Signing boot image with verity keys" ) val signed = File.createTempFile("signed" , ".img" , context.cacheDir) try { val src = newBoot.newInputStream().buffered() val out = signed.outputStream().buffered() withStreams(src, out ) { _, _ -> SignBoot.doSignature(null , null , src, out , "/boot" ) } } catch (e: IOException) { console.add("! Unable to sign image" ) Timber.e(e) return false } job.add("cat $signed > $newBoot " , "rm -f $signed " ) } job.exec() return true }
具体逻辑在 boot_patch.sh 里
Magisk\scripts\boot_patch.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ..... ui_print "- Patching ramdisk" echo "KEEPVERITY=$KEEPVERITY " > configecho "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT " >> configecho "PATCHVBMETAFLAG=$PATCHVBMETAFLAG " >> configecho "RECOVERYMODE=$RECOVERYMODE " >> config[ ! -z $SHA1 ] && echo "SHA1=$SHA1 " >> config SKIP32="#" SKIP64="#" if [ -f magisk32 ]; then ./magiskboot compress=xz magisk32 magisk32.xz unset SKIP32 fi if [ -f magisk64 ]; then ./magiskboot compress=xz magisk64 magisk64.xz unset SKIP64 fi ./magiskboot cpio ramdisk.cpio \ "add 0750 $INIT magiskinit" \"mkdir 0750 overlay.d" \"mkdir 0750 overlay.d/sbin" \"$SKIP32 add 0644 overlay.d/sbin/magisk32.xz magisk32.xz" \"$SKIP64 add 0644 overlay.d/sbin/magisk64.xz magisk64.xz" \"patch" \"backup ramdisk.cpio.orig" \"mkdir 000 .backup" \"add 000 .backup/.magisk config" rm -f ramdisk.cpio.orig config magisk*.xz.....
从 add 0750 $INIT magiskinit
可以看出来,magisk 对内核的修补其实是用 magisk 替换了系统的 init 文件,添加 magisk64.xz 或 magisk32.xz 文件.
并且把原 init 文件放置到.backup 目录下 (这部分代码我没找到,但是 magiskinit 在代码里可以看出来).
magiskinit 注入 init.rc
Magisk\native\jni\init\init.cpp
主要逻辑如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int main (int argc, char *argv[]) {....... if (argc > 1 && argv[1 ] == "selinux_setup" sv) { init = new SecondStageInit (argv); } else { load_kernel_info (&config); if (config.skip_initramfs) init = new LegacySARInit (argv, &config); else if (config.force_normal_boot) init = new FirstStageInit (argv, &config); else if (access ("/sbin/recovery" , F_OK) == 0 || access ("/system/bin/recovery" , F_OK) == 0 ) init = new RecoveryInit (argv, &config); else if (check_two_stage ()) init = new FirstStageInit (argv, &config); else init = new RootFSInit (argv, &config); } init->start (); exit (1 ); }
结合日志分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [ 0.317377] magiskinit: Device config: [ 0.317386] magiskinit: skip_initramfs=[0] [ 0.317390] magiskinit: force_normal_boot=[1] [ 0.317394] magiskinit: rootwait=[1] [ 0.317397] magiskinit: slot=[_b] [ 0.317401] magiskinit: dt_dir=[/proc/device-tree/firmware/android] [ 0.317404] magiskinit: fstab_suffix=[default] [ 0.317407] magiskinit: hardware=[qcom] [ 0.317409] magiskinit: hardware.platform=[] [ 0.317412] magiskinit: emulator=[0] [ 0.317415] magiskinit: FirstStageInit [ 0.317783] magiskinit: Replace [/system/bin/init] -> [/data/magiskinit] [ 0.324376] magiskinit: Unmount [/sys] [ 0.324441] magiskinit: Unmount [/proc] [ 0.792166] magiskinit: SecondStageInit [ 0.794063] magiskinit: patch_ro_root [ 0.794109] magiskinit: Setup Magisk tmp at /dev/0hsz [ 0.798367] magiskinit: Setup userdata: [sda38] (259, 22) [ 0.799558] magiskinit: mount /dev/0hsz/.magisk/block/data->/dev/0hsz/.magisk/mirror/data failed with 22: Invalid argument [ 0.873824] magiskinit: Setup metadata: [sda17] (259, 1) [ 0.883925] magiskinit: Inject magisk services: [ryq6EKuI] [VFzLTmmD] [ 0.890089] magiskinit: Replace [d30138f2310a9fb9c54a3e0c21f58591] -> [L5mAeRz9] [ 0.897612] magiskinit: Replace [d30138f2310a9fb9c54a3e0c21f58591] -> [L5mAeRz9] [ 0.904678] magiskinit: Hijack [/sys/fs/selinux/load] [ 0.904694] magiskinit: Hijack [/sys/fs/selinux/enforce] [ 0.905782] magiskinit: Load custom sepolicy patch: [/dev/0hsz/.magisk/mirror/metadata/magisk/zygisk_shamiko/sepolicy.rule] [ 0.906361] magiskinit: Mount [.magisk/rootdir/system/etc/init/hw/init.rc] -> [/system/etc/init/hw/init.rc] [ 0.906400] magiskinit: Unmount [/dev/0hsz/.magisk/mirror/system_root] [ 0.906409] magiskinit: Unmount [/dev/0hsz/.magisk/mirror/metadata] [ 1.018016] magiskinit: Load policy from: .magisk/selinux/load
从日志可以分析,我的 magiskinit 执行了 FirstStageInit 和 SecondStageInit, 那就以这俩函数开始分析
先说明下,magiskinit 是以代理系统 init 的方式进行的,所以有 FirstStageInit 和 SecondStageInit 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class FirstStageInit : public BaseInit {private : void prepare () ; public : FirstStageInit (char *argv[], BootConfig *config) : BaseInit (argv, config) { LOGD ("%s\n" , __FUNCTION__); }; void start () override { prepare (); exec_init (); } }; class SecondStageInit : public SARBase {private : bool prepare () ; public : SecondStageInit (char *argv[]) : SARBase (argv) { setup_klog (); LOGD ("%s\n" , __FUNCTION__); }; void start () override { if (prepare ()) patch_rw_root (); else patch_ro_root (); exec_init (); } };
FirstStageInit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ...... #define INIT_PATH "/system/bin/init" #define REDIR_PATH "/data/magiskinit" void FirstStageInit::prepare () { xmkdirs ("/data" , 0755 ); xmount ("tmpfs" , "/data" , "tmpfs" , 0 , "mode=755" ); cp_afc ("/init" , REDIR_PATH); unlink ("/init" ); const char *orig_init = backup_init (); if (access (orig_init, F_OK) == 0 ) { xrename (orig_init, "/init" ); } else { ...... } { auto init = mmap_data ("/init" , true ); init.patch ({ make_pair (INIT_PATH, REDIR_PATH) }); } ...... }
SecondStageInit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #define ROOTMIR MIRRDIR "/system_root" #define NEW_INITRC "/system/etc/init/hw/init.rc" void SARBase::patch_ro_root () { string tmp_dir; LOGD ("patch_ro_root\n" ); if (access ("/sbin" , F_OK) == 0 ) { tmp_dir = "/sbin" ; } else { char buf[8 ]; gen_rand_str (buf, sizeof (buf)); tmp_dir = "/dev/" s + buf; xmkdir (tmp_dir.data (), 0 ); } setup_tmp (tmp_dir.data ()); chdir (tmp_dir.data ()); ...... if (access (NEW_INITRC, F_OK) == 0 ) { xmkdirs (dirname (ROOTOVL NEW_INITRC), 0755 ); patch_init_rc (NEW_INITRC, ROOTOVL NEW_INITRC, tmp_dir.data ()); } else { patch_init_rc ("/init.rc" , ROOTOVL "/init.rc" , tmp_dir.data ()); } extract_files (false ); ...... magic_mount (ROOTOVL); ...... chdir ("/" ); } static void extract_files (bool sbin) { const char *m32 = sbin ? "/sbin/magisk32.xz" : "magisk32.xz" ; const char *m64 = sbin ? "/sbin/magisk64.xz" : "magisk64.xz" ; auto magisk = mmap_data (m32); unlink (m32); int fd = xopen ("magisk32" , O_WRONLY | O_CREAT, 0755 ); unxz (fd, magisk.buf, magisk.sz); close (fd); patch_socket_name ("magisk32" ); if (access (m64, F_OK) == 0 ) { magisk = mmap_data (m64); unlink (m64); fd = xopen ("magisk64" , O_WRONLY | O_CREAT, 0755 ); unxz (fd, magisk.buf, magisk.sz); close (fd); patch_socket_name ("magisk64" ); xsymlink ("./magisk64" , "magisk" ); } else { xsymlink ("./magisk32" , "magisk" ); } dump_manager ("stub.apk" , 0 ); }
这一部分的主要 patch init.rc 注入 magisk 服务,用 dev/xxx/.magisk/rootdir/system/etc/init/hw/init.rc
替换原系统的 init.rc
修改后的 init.rc.
cat dev/xxx/.magisk/rootdir/system/etc/init/hw/init.rc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ...... on post-fs-data start logd rm /dev/.magisk_unblock start 3dgv8Xp9rdRtMv wait /dev/.magisk_unblock 40 rm /dev/.magisk_unblock service 3dgv8Xp9rdRtMv /dev/BYdnLYi/magisk --post-fs-data user root seclabel u:r:magisk:s0 oneshot service OPIx4M5Px /dev/BYdnLYi/magisk --service class late_start user root seclabel u:r:magisk:s0 oneshot on property:sys.boot_completed=1 exec /dev/BYdnLYi/magisk --boot-complete on property:init.svc.zygote=restarting exec /dev/BYdnLYi/magisk --zygote-restart on property:init.svc.zygote=stopped exec /dev/BYdnLYi/magisk --zygote-restart
新增了 magisk 服务.
magiskinit 代理 init 进程
之前说过,在 magisk 里 patch 了原 init 的 /system/bin/init
为 /data/magiskinit
所以原系统 init 的 execv("/system/bin/init")
被改成 execv("/data/magiskinit")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int byte_data::patch (bool log, str_pairs list) { if (buf == nullptr ) return 0 ; int count = 0 ; for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) { for (auto [from, to] : list) { if (memcmp (p, from.data (), from.length () + 1 ) == 0 ) { if (log) LOGD ("Replace [%s] -> [%s]\n" , from.data (), to.data ()); memset (p, 0 , from.length ()); memcpy (p, to.data (), to.length ()); ++count; p += from.length (); } } } return count; }
可以看到,用 memcmp
比较,然后替换.
magisk 代理 init 的过程如下:
1 2 3 4 5 6 ...... const char *orig_init = backup_init (); if (access (orig_init, F_OK) == 0 ) { xrename (orig_init, "/init" ); } ......
1 2 3 4 5 6 7 8 9 void BaseInit::exec_init () { for (auto &p : reversed (mount_list)) { if (xumount2 (p.data (), MNT_DETACH) == 0 ) LOGD ("Unmount [%s]\n" , p.data ()); } execv ("/init" , argv); exit (1 ); }
好了,就先到这里吧.