Magisk on T1

Smartisan T1是一款老机型了,时至今日,运行的仍然是Android 4.4的系统。以前研究过它的Bootloader解锁,如今打算回头再看看怎么把最新的root方案Magisk移植进来实现持久。

不同于supersu需要twrp的支持,Magisk提供了一种十分便捷的修改镜像的方法。只要把目标设备的boot.img拖出来,放到任何一台安装有Magisk Manager的手机上,就能进行重打包,包装成一个带有root功能的镜像。接下来,无论用什么方法,只要能把boot.img刷回boot分区就可以了。

临时root

T1升到最新版本后(2018年2月份的版本),为了把boot.img拖出来,并放回去,需要取得一个临时的root权限,我利用的是CVE-2017-8890,不过这不是重点。问题是后面的Magisk重打包。

Magisk原理

Magisk的重打包,从底层看,主要就是把/init替换成了它自己的magiskinit。当boot启动时,magiskinit首先从/.backup恢复原始的init文件。然后从magiskinit中释放出来magisk.bin和Manager的APK等后续建立root需要的文件。接着它会以init权限patch sepolicy,增加一个名为magisk的上下文环境,最后启动magiskd等待su的连接。

当su命令执行时,背后实际上是向magiskd请求root,magisk则会向Magisk Manager发起问询,等待用户确认后才赋予放行,最后magiskd启动一个shell,并把它重定向到su通过unix socket发送过来的输入输出句柄上。

T1带来的麻烦

现在最大的问题是Magisk Manager编译时只能运行在5.0以上的系统,4.4没有支持。如果抛弃magisk,单靠suid之不能建立起完整root的,4.4虽然selinux弱得很(通过/proc/self/attr/current就能任意修改自身上下文),但普通的用户进程其capability已经几乎全部丢弃,即便uid和gid都为0,能做的事情也有限。还是需要一套magisk这种劫持启动流程的方案才能从一开始就拿到init。

所以打算完全抛弃Magisk Manager,通过patch magisk自身的逻辑,实现任意进程执行su时无需校验就放行。

patched_boot.img的重打包

想要patch magisk,要修改的实际上是Manager重打包后的patched_boot.img。它当中的magiskinit包含了magiskd的代码,只要修改两个关键的跳转就算通过了。不过问题是这里每个环节都很繁琐。首先magiskinit的文件结构很特殊:

$ binwalk init

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 32-bit LSB executable, ARM, version 1 (SYSV)
262482        0x40152         Unix path: /vendor/etc/selinux/precompiled_sepolicy
262824        0x402A8         Unix path: /sys/fs/selinux/policy
262895        0x402EF         Unix path: /sys/fs/selinux/load
263576        0x40598         Unix path: /sys/fs/selinux/policy)
266224        0x40FF0         Unix path: /vendor/etc/selinux/plat_sepolicy_vers.txt
296964        0x48804         xz compressed data
301647        0x49A4F         xz compressed data
346435        0x54943         xz compressed data
376768        0x5BFC0         Unix path: /proc/device-tree/firmware/android

其中0x49A4F开始的这个区块就是magiskd也就是su_daemon。由于magiskinit要根据这些偏移解出来所需要的文件,修改的时候必须要保证文件大小精确一致。
0x49A4F 文件可以dd出来,然后unxz解开。修改二进制后,不能直接xz,xz命令和magisk用的结构稍微不太一样,需要使用python3的lzma的compress去压缩,也就是:

lzma.compress(magisk, preset=9, check=lzma.CHECK_NONE)

dd回去之后,还要进行ramdisk的重打包,这里同样有个文件大小要保持一致的问题。因为patched_boot.img已经和boot.img不一样了,末尾会增加一个xz,包含Manager的APK文件,虽然用不到,但虽已覆盖会导致初始化失败。

虽然ramdisk重打包有很多工具,但都无法和magisk的cpio保持一致,主要原因是普通的重打包都没有考虑到/.backup等隐藏目录的引入。所以更精确的做法是直接对cpio文件操作,找到init文件的位置,写入修改后的文件内容。对cpio进行gzip时同样还有问题,系统自带的gzip会加入过多的metadata导致文件比原来要大。我解决的方法是minigzip.py,去掉文件名相关的metadata,然后进行压缩,这样大小就和原来相同了,最后dd回patched_boot.img就可以了。再次开机启动就拿到了久违的su:

shell@msm8974sfo:/ # id -Z
uid=0(root) gid=0(root) context=u:r:magisk:s0

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注