Magisk

内部细节

文件结构

“Magisk tmpfs目录”中的路径

Magisk挂载一个tmpfs 目录来存储一些临时数据。对于具有/sbin文件夹的设备,将选择它,因为它将用作将二进制文件注入PATH的覆盖层。从Android 11 开始,/sbin可能不存在,所以Magisk将在/dev下随机创建一个文件夹并将其用作基本文件夹。

# 为了获得Magisk当前正在使用的基本文件夹,
# 使用命令`magisk --path`.
# 诸如magisk, magiskinit, 之类的二进制文件
# 以及所有指向指令的符号链接都直接存储在此路径中。
# 这意味着当它为/sbin时,这些二进制文件将直接位于PATH中。
MAGISKPATH=$(magisk --path)

# Magisk内部物品
MAGISKTMP=$MAGISKBASE/.magisk

# Magisk的BusyBox目录。 在此文件夹中存储busybox二进制文件和指向
# 其所有小程序的符号链接。
# 不建议使用此目录,请直接调用/data/adb/magisk/busybox 
# 并使用BusyBox的ASH独立模式。
# 将来将删除此路径的创建。
$MAGISKTMP/busybox

# /data/adb/modules 将在此处绑定安装。
# 由于nosuid挂载标志,未使用原始文件夹。
$MAGISKTMP/modules

# 当前的Magisk安装配置
$MAGISKTMP/config

# 分区镜像
# 此路径中的每个目录都将
# 使用其目录名称的分区进行挂载。
# 例如 system, system_ext, vendor, data ...
$MAGISKTMP/mirror

# Magisk在内部创建块设备以安装镜像。
$MAGISKTMP/block

# 根目录补丁文件
# 在 system-as-root 设备上, / 不可写.
# 所有预初始化的修补文件都存储在此处并绑定安装。
$MAGISKTMP/rootdir

/data中的路径

一些二进制文件和文件应存储在/data.的非易失性存储中。为了防止被检测到,所有东西都必须存储在安全的地方,并且在/data中无法被检测到。选择该文件夹/data/adb是因为具有以下优点:

SECURE_DIR=/data/adb

# 储存一般post-fs-data脚本的文件夹
$SECURE_DIR/post-fs-data.d

# 储存一般late_start service脚本的文件夹
$SECURE_DIR/service.d

# Magisk模块
$SECURE_DIR/modules

# 等待升级的Magisk模块
# 在挂载时修改模块文件是不安全的
# 通过Magisk应用安装的模块将存储在此处
# 并在下一次重新启动时合并到$SECURE_DIR/modules
$SECURE_DIR/modules_update

# 数据库存储设置和root权限
MAGISKDB=$SECURE_DIR/magisk.db

#所有与magisk相关的二进制文件,包括busybox、脚本和magisk二进制文件。
#用于支持模块安装,addon.d、Magisk app等。
DATABIN=$SECURE_DIR/magisk

Magisk启动过程

预初始化

magiskinit将替换init作为第一个要运行的进程。

post-fs-data

/data被解密和挂载时,这会在post-fs-data上触发。将启动守护进程magiskd,执行post-fs-data脚本,并magic挂载模块文件

late_start

在启动过程的后期,late_start将被触发,Magisk“服务”模式将被启动。在此模式下,执行服务脚本。

Resetprop

通常,系统属性被设计为只能通过init来更新,并且对于非根进程是只读的。使用root可以通过使用诸如setprop之类的命令向 property_service(由init托管)发送请求来更改属性,但是仍然禁止更改只读属性(以ro.开头的属性,例如ro.build.product)和删除属性。

resetprop是通过从AOSP中提取出与系统属性相关的源代码来实现的,并通过修补来允许直接修改property area或prop_area,而无需经过prop_area。由于我们绕过了prop_area,因此需要注意一些事项:

Magic挂载

Magic挂载的实际实现和算法的细节在这里被省略了,如果有兴趣,请直接钻研源代码(core/module.cpp)。

尽管安装逻辑非常复杂,但Magic挂载的最终结果实际上非常简单。对于每个模块,目录$MODPATH/system将被递归地合并到真实的/system中;即:将真实system中的现有文件替换为模块“system”中的文件,并将模块“system”中的新文件添加到真实system中。

你还可以使用一个额外的技巧:如果你在模块系统的任何文件夹中放置一个名为.replace的空文件,而不是合并内容,该文件夹将直接替换实际系统中的文件。在某些情况下,这将非常方便,例如交换一个系统应用程序。

如果你想替换/vendor/product目录下的文件,请将它们放在$MODPATH/system/vendor$MODPATH/system/product目录下。Magisk将透明地处理这两种情况,无论vendor或product是否是一个单独的分区。

SELinux策略

Magisk将修补sepolicy,以确保root和Magisk操作可以以安全的方式进行。新的域magisk是有效的,magiskd和所有的root shell都将运行在这个域中。 magisk_file是一种新的文件类型,它被设置为允许每个域(不受限制的文件上下文)访问。

在Android 8.0之前,所有允许的su客户端域都允许直接连接到magiskd,并与守护进程建立连接以获得远程root shell。Magisk还必须放宽一些ioctl操作,这样root shell才能正常工作。

在Android 8.0之后,为了减少对Android沙箱中规则的放宽,我们部署了一个新的SELinux模型。magisk二进制文件被标记为magisk_exec类型文件,作为允许的su客户端域运行的进程执行magisk二进制文件(包括su命令)将通过使用magisk_client规则传输到type_transition。规则严格限制只有magisk域进程才允许将文件归为magisk_exec。不允许直接连接magiskd的接口;访问这个守护进程的唯一方法是通过magisk_client进程。这些更改使我们可以保持沙盒的完整性,并将Magisk特定的规则与其他策略分开。

完整的规则集可以在magiskpolicy/rules.cpp中找到。

原文