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
是因为具有以下优点:
700
,所有者为root
,因此非root进程无法以任何可能的方式进入,读取和写入文件夹。u:object_r:adb_data_file:s0
,并且几乎没有进程有权与该上下文进行任何交互。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
magiskinit
将替换init
作为第一个要运行的进程。
init
来为我们挂载分区。/sepolicy
加载sepolicy,在vendor中预编译sepolicy,或编译split sepolicy/sepolicy
或/sbin/.se
或/dev/.se
init
或libselinux.so
进行补丁,强制系统加载补丁策略init.rc
init
以继续引导进程当/data
被解密和挂载时,这会在post-fs-data
上触发。将启动守护进程magiskd
,执行post-fs-data脚本,并magic挂载模块文件
在启动过程的后期,late_start
将被触发,Magisk“服务”模式将被启动。在此模式下,执行服务脚本。
通常,系统属性被设计为只能通过init
来更新,并且对于非根进程是只读的。使用root可以通过使用诸如setprop
之类的命令向 property_service
(由init
托管)发送请求来更改属性,但是仍然禁止更改只读属性(以ro.
开头的属性,例如ro.build.product
)和删除属性。
resetprop
是通过从AOSP中提取出与系统属性相关的源代码来实现的,并通过修补来允许直接修改property area或prop_area
,而无需经过prop_area
。由于我们绕过了prop_area
,因此需要注意一些事项:
on property:foo=bar
如果属性更改没有通过property_service
进行,则不会触发*.rc
脚本。resetprop
的默认设置属性行为与setprop
匹配,这将触发事件(通过首先删除属性,然后通过property_service
设置它来实现)。如果需要这种特殊行为,可以使用-n
标记禁用它。persist.
开头的props,例如persist.sys.usb.config
)都储存在prop_area
和/data/property
中。默认情况下,删除props不会从持久存储中删除它,这意味着属性将在下一次重启后恢复;读取props不会从持久存储中读取,这是因为getprop
的进程。使用-p
标志,删除props将同时删除prop_area
和/data/property
, 的props,读取props将同时从prop_area
和持久存储中读取。Magic挂载的实际实现和算法的细节在这里被省略了,如果有兴趣,请直接钻研源代码(core/module.cpp
)。
尽管安装逻辑非常复杂,但Magic挂载的最终结果实际上非常简单。对于每个模块,目录$MODPATH/system
将被递归地合并到真实的/system
中;即:将真实system中的现有文件替换为模块“system”中的文件,并将模块“system”中的新文件添加到真实system中。
你还可以使用一个额外的技巧:如果你在模块系统的任何文件夹中放置一个名为.replace
的空文件,而不是合并内容,该文件夹将直接替换实际系统中的文件。在某些情况下,这将非常方便,例如交换一个系统应用程序。
如果你想替换/vendor
或/product
目录下的文件,请将它们放在$MODPATH/system/vendor
或$MODPATH/system/product
目录下。Magisk将透明地处理这两种情况,无论vendor或product是否是一个单独的分区。
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
中找到。