Magisk

开发者指南

此指南由coolapk@依然的爱翻译

翻译不易,如果这篇指南帮到了你,你可以给我捐赠(当然,不捐赠也没关系)

BusyBox

Magisk附带了功能完整的BusyBox二进制文件(包括对SELinux的完整支持)。执行文件位于/data/adb/magisk/busybox。Magisk的BusyBox支持运行时可切换的“ASH Standalone Shell Mode(ASH独立Shell模式)”。这种独立模式的意思是,在ashshell的中的BusyBox运行时,无论PATH的值为何,每个命令都将直接使用BusyBox中的应用(子命令)。例如,就像lsrmchmod 命令一样。它们将不在使用PATH(在Android中,默认为/system/bin/ls/system/bin/rm,和/system/bin/chmod。),而是直接调用Magisk内部的BusyBox应用(子命令)。这样可以确保脚本始终在可预测的环境中运行,并且无论运行在哪个Android版本上,始终具有完整的命令集。要强制命令不使用BusyBox,必须使用完整路径调用可执行文件。

在启用了ash的独立模式的情况下,Magisk中运行的每个单独的shell脚本都将在内部的BusyBox的shell中执行。对于与第三方开发者而言,这包括所有引导脚本和模块安装脚本。

对于那些想在Magisk之外使用此“独立模式”功能的人,有两种启用它的方法:

  1. 将环境变量ASH_STANDALONE设置为1
    示例: ASH_STANDALONE=1 /data/adb/magisk/busybox sh <script>
  2. 切换命令行选项:
    /data/adb/magisk/busybox sh -o standalone <script>

为确保所有后续的sh执行的shell也都以独立模式运行,推荐使用1方案(这是Magisk和Magisk应用程序内部使用的方法),因为环境变量一直继承到子进程。

Magisk模块

Magisk模块的文件在/data/adb/modules中具有以下结构:

/data/adb/modules
├── .
├── .
|
├── $MODID                  <--- 该文件夹以模块的ID命名
│   │
│   │      *** 模块ID ***
│   │
│   ├── module.prop         <--- 该文件存储模块的基本信息
│   │
│   │      *** 主要内容 ***
│   │
│   ├── system              <--- 如果skip_mount不存在,则将挂载此文件夹
│   │   ├── ...
│   │   ├── ...
│   │   └── ...
│   │
│   │      *** 状态标志 ***
│   │
│   ├── skip_mount          <--- 如果存在,Magisk将不会挂载你的system文件夹
│   ├── disable             <--- 如果存在,该模块将被禁用
│   ├── remove              <--- 如果存在,该模块将在下次重新启动时被删除
│   │
│   │      *** 可选文件 ***
│   │
│   ├── post-fs-data.sh     <--- 该脚本将在post-fs-data模式下执行
│   ├── service.sh          <--- 该脚本将在late_start service模式执行
|   ├── uninstall.sh        <--- 当Magisk删除您的模块时,将执行此脚本
│   ├── system.prop         <--- 该文件中的properties将通过resetprop作为系统properties加载
│   ├── sepolicy.rule       <--- 添加自定义的sepolicy规则
│   │
│   │      *** 自动生成,请勿手动创建或修改 ***
│   │
│   ├── vendor              <--- 指向$MODID/system/vendor的链接
│   ├── product             <--- 指向$MODID/system/product的链接
│   ├── system_ext          <--- 指向$MODID/system/system_ext的链接
│   │
│   │      *** 允许任何其他文件/文件夹 ***
│   │
│   ├── ...
│   └── ...
|
├── another_module
│   ├── .
│   └── .
├── .
├── .

module.prop

这是标准的module.prop格式

id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>

Shell脚本(*.sh)

请阅读启动脚本部分,了解post-fs-data.shservice.sh之间的差异。对于大多数模块开发者,service.sh如果只需运行启动脚本那便足够了。

在所有的模块脚本中,请使用MODDIR=${0%/*}来获取模块的基本目录信息;千万不要在脚本中编码模块目录。

system.prop

该文件的格式与build.prop相同。每行包含[key]=[value]

sepolicy.rule

如果您的模块需要一些其他的sepolicy补丁,请将这些规则添加到此文件中。 模块安装程序脚本和Magisk的守护进程将确保将此文件复制到magiskinit,它可以读取预初始化的位置,以确保正确插入这些规则。

该文件中的每一行都将被视为策略声明。要详细了解策略声明的格式,请查看magiskpolicy的文档

system文件夹

您希望Magisk为您替换/插入的所有文件都应放在此文件夹中。请查看Magic挂载 部分,以了解Magisk如何挂载文件。

Magisk模块安装程序

Magisk模块安装程序是打包成zip文件的Magisk模块,可以在Magisk应用程序或第三方recovery(如TWRP)中进行刷入。安装程序的文件结构与Magisk模块的文件结构相同(请查看上一部分以获取更多信息)。最简单的Magisk模块安装程序只是一个打包为zip文件的Magisk模块,此外还包含以下文件:

默认情况下,update-binary将检查/设置环境,加载使用的脚本,将模块安装程序的zip解压缩到将要安装模块的位置,最后执行一些琐碎的任务和清理工作,这些工作和清理应满足大多数简单模块的需求。

module.zip
│
├── META-INF
│   └── com
│       └── google
│           └── android
│               ├── update-binary      <--- 你下载的module_installer.sh
│               └── updater-script     <--- 应该只包含字符串"#MAGISK"
│
├── customize.sh                       <--- (可选,稍后会有更多详细信息)
│                                           该脚本将通过update-binary来获取
├── ...
├── ...  /* 模块的其余文件 */
│

自定义

如果您需要自定义模块安装过程,则可以选择在安装程序中创建一个名为customize.sh的脚本。这个脚本会在所有文件以默认permissions和secontext应用后,由update-binary调用(不是执行!)。如果您的模块包含基于ABI的其他文件,或者您需要为某些文件(例如/system/bin中的文件),设置特殊的permissions/secontext,这将非常有用。

如果您需要更多的自定义,并且希望自己做所有事情请在customize.sh中标注SKIPUNZIP=1以跳过提取操作并应用默认permissions/secontext。请注意,这样做后,你的customize.sh将负责自行安装所有内容。

customize.sh的运行环境

这个脚本将在启用了“独立模式”Magisk的BusyBox ash shell 中运行。为了方便起见,可以使用以下变量和shell函数:

变量
可用函数

ui_print <msg>
    打印(print)<msg>到控制台
    避免使用'echo',因为它不会显示在第三方的recovery的控制台中。

abort <msg>
    打印错误信息<msg>到控制台并终止安装
    避免使用'exit',因为它会跳过终止的清理步骤

set_perm <文件名> <所有者> <用户组> <文件权限> [上下文]
    如果 [上下文] 没有设置,则默认为"u:object_r:system_file:s0"
    该函数是以下命令的简写:
       chown 所有者.用户组 文件名
       chmod 权限 文件名
       chcon 上下文 文件名

set_perm_recursive <目录> <所有者> <用户组> <目录权限> <文件权限> [上下文]
    如果 [上下文] 没有设置,则默认为"u:object_r:system_file:s0"
    对于<目录>中的所有文件,它将调用:
       set_perm 文件 所有者 用户组 文件权限 上下文
    对于<目录>中的所有目录(包括自身),它将调用:
       set_perm 目录 所有者 用户组 目录权限 上下文
简单的替换

您可以在变量名中声明要直接替换的文件夹列表REPLACE。模块安装程序脚本将提取此变量并创建.replace文件进行替换。声明示例:

REPLACE="
/system/app/YouTube
/system/app/Bloatware
"

上面的列表将导致创建以下文件: $MODPATH/system/app/YouTube/.replace$MODPATH/system/app/Bloatware/.replace

笔记

提交模块

你可以将模块提交到Magisk-Module-Repo以便用户可以直接在Magisk Manager中下载你的模块。

模块技巧

删除文件

如何自动删除文件?实际使文件删除很复杂(可能,不值得付出努力)。用空文件替换它应该已经足够了!创建一个具有相同名称的空文件,并将其放置在模块内的相同路径中,它将用空文件替换你的目标文件。

删除文件夹

与上述相同,实际上使文件夹删除是不值得的。用一个空文件夹替换它应该已经足够了!对于模块开发人员来说,一个方便的技巧是将要删除的文件夹添加到customize.sh中的REPLACE列表中。如果您的模块没有提供相应的文件夹,它将创建一个空文件夹,并自动将.replace添加到该空文件夹中,以便虚拟文件夹可以正确替换/system中的一个文件夹。

启动脚本

在Magisk中,您可以以两种不同的模式运行启动脚本: post-fs-datalate_start service模式

在Magisk中,还有两种脚本: 通用脚本模块脚本.

这些脚本将在启用了“独立模式”Magisk的BusyBox ash shell中运行.

根目录覆盖系统

由于/在system-as-root设备上是只读的, 因此Magisk提供了一个覆盖系统,使开发人员可以替换根目录中的文件或添加新的*.rc脚本。此功能主要是为自定义内核开发人员设计的。

覆盖文件应放在boot镜像ramdisk的overlay.d文件夹中并遵循以下规则:

  1. overlay.d中所有的*.rc将在init.rc之后读取并链接
  2. 现有文件可以被位于相同相对路径的文件替换
  3. 与不存在的文件对应的文件将被忽略

为了在自定义*.rc脚本中引用其他文件, 请在overlay.d/sbin中添加它们。上面的3条规则不适用于此特定文件夹中的所有内容,因为它们将直接复制到Magisk的内部tmpfs目录(该目录始终位于/sbin)。

由于Android 11中的更改​​,不再保证/sbin 文件夹的存在。这种情况下,Magisk会随机生成tmpfs文件夹。在你的*.rc脚本中所有的${MAGISKTMP}会在magiskinit注入到init.rc中时,被Magisk tmpfs文件夹替换。这也可以在Android 11之前的设备上使用,因为在这种情况下${MAGISKTMP}将简单地被替换为/sbin,因此最佳实践是在引用其他文件时,不要*.rc脚本中编码/sbin

这是一个如何使用自定义*.rc脚本设置overlay.d的示例:

ramdisk
│
├── overlay.d
│   ├── sbin
│   │   ├── libfoo.ko      <--- 这两个文件将被复制
│   │   └── myscript.sh    <--- 到Magisk的tmpfs目录
│   ├── custom.rc          <--- 该文件将被注入到init.rc中
│   ├── res
│   │   └── random.png     <--- 该文件将替换/res/random.png
│   └── new_file           <--- 该文件将被忽略,因为
│                               /new_file不存在
├── res
│   └── random.png         <--- 该文件将被替换为
│                               /overlay.d/res/random.png
├── ...
├── ...  /* 其余的initramfs文件 */
│

这是一个示例custom.rc:

# 使用${MAGISKTMP}引用Magisk的tmpfs目录

on early-init
    setprop sys.example.foo bar
    insmod ${MAGISKTMP}/libfoo.ko
    start myservice

service myservice ${MAGISKTMP}/myscript.sh
    oneshot

原文