我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Magisk内部实现原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Magisk内部实现原理

在这里插入图片描述

Android10以后,Android系统限制了System分区的修改,结果就是,即使你i是自己编译的Android系统,即使是有做高的root权限,你依然无法挂载System分区并对其内容进行修改,尽管网上有各种帖子说可以使用mount -o rw,remount /,但这并没有解决开发者的实际问题。这意味着传统的万能root出现局限性,为了解决这个问题Magisk的作者Top John Wu推文中确认了这一问题,并确认了导致该问题的原因是谷歌在Android10以后引入了EXT4 共享块,而这个共享块和其他分区的区别在于根本没有可用空间的概念,所以也就没办法挂在为可读写。为了解决这个问题,Magisk团队发现可以重定向文件读取时的文件路径来实现修改的目的,这似乎和我们Hook有相似之处,而这个,被Top John Wu称为Systemless(无System分区)而由此概念引申出来的Root方案,也叫做systemless root。。

Magisk文件结构

Magisk系统包含上层的控制App以及下层的可执二进制文件以及一些相关配置或者数据文件。我们从底层往上层看会更容易明白Magisk的功能构成和架构思维。

首先,Magisk 会挂载一个tmpfs目录来存放一些临时数据。在Android11以下,这个目录时sbin,从 Android 11 开始,/sbin文件夹可能不存在,那么 Magisk 会在/dev下随机创建一个文件夹并将其作为Magisk的Root文件夹。

放在sbin或者dev下的原因是:/sbin或/dev目录非su权限不可读,因此第三方APP无法检测。

我们可以通过在adb shell下使用magisk --path打印当前Magisk使用的目录:

找到magisk目录

blueline:/ # magisk --path/dev/bNpnxq

文件列表

接下来我们看下这个目录下都有什么文件,以及如何解读这些文件;

blueline:/dev/bNpnxq # ls -altotal 720drwx------  3 root    root       200 2022-12-24 21:41 .drwxr-xr-x 24 root    root      6200 2022-12-25 01:40 ..drwxr-xr-x  8 root    root       180 2022-12-24 21:41 .magisklrwxrwxrwx  1 root    root        10 1970-02-17 10:04 magisk -> ./magisk64-rwxr-xr-x  1 root    root    154452 1970-02-17 10:04 magisk32-rwxr-xr-x  1 root    root    247168 1970-02-17 10:04 magisk64-rwxr-xr-x  1 u0_a206 u0_a206 328240 2022-12-24 21:41 magiskpolicylrwxrwxrwx  1 root    root         8 1970-02-17 10:04 resetprop -> ./magisklrwxrwxrwx  1 root    root         8 1970-02-17 10:04 su -> ./magisklrwxrwxrwx  1 root    root        14 1970-02-17 10:04 supolicy -> ./magiskpolicy

可以看到除了magiskpolicy以外,其他都属于root用户组。

下面的注释说得很清楚了,直接看代码就好了。

# 想要获取 Magisk 正在使用的当前Base文件夹,请使用命令 `magisk --path`。# 像 magisk、magiskinit 这样的二进制文件和所有指向小程序的符号链接都直接存储在这个路径中。 # 这意味着当这是 /sbin 时,这些二进制文件将直接位于 PATH 中。MAGISKBASE=$(magisk --path)# Magisk 内部文件MAGISKTMP=$MAGISKBASE/.magisk# Magisk 的 BusyBox 目录。 # 在此文件夹中存储 busybox 二进制文件和指向其所有小程序的符号链接。 # 此目录的任何用法已弃用,请直接调用 /data/adb/magisk/busybox 并使用 BusyBox 的 ASH 独立模式。# 将来会删除此路径的创建。$MAGISKTMP/busybox# /data/adb/modules 将mount在这里。 原始文件夹未被使用(由于 nosuid 挂载标志)。$MAGISKTMP/modules# 当前的 Magisk 安装配置$MAGISKTMP/config# 分区镜像# 应用在访问系统文件的时候会优先访问该目录下的文件,以达到狸猫换太子的目的# 例如 system,system_ext,vendor,data......$MAGISKTMP/mirror# Magisk 内部创建block设备来挂载镜像,和mirror对应,对应关系可通过ls -al列出$MAGISKTMP/block# Root 目录补丁文件# 位于 system-as-root 设备上,/ 不可写。# 所有预初始化补丁文件都放在这里并mount$MAGISKTMP/rootdir

下面是/dev/bNpnxq/.magisk/mirror列出的文件列表,对应block的设备节点

blueline:/dev/bNpnxq/.magisk/block # ls -altotal 0d--------- 2 root root      160 2022-12-24 21:41 .drwxr-xr-x 8 root root      180 2022-12-24 21:41 ..brw------- 1 root root 253,   8 2022-12-24 21:41 databrw------- 1 root root 259,   4 1970-02-17 10:04 metadatabrw------- 1 root root 253,   7 2022-12-24 21:41 productbrw------- 1 root root 253,   5 2022-12-24 21:41 system_extbrw------- 1 root root 253,   4 2022-12-24 21:41 system_rootbrw------- 1 root root 253,   6 2022-12-24 21:41 vendorblueline:/dev/bNpnxq/.magisk/block # cd ../mirror/blueline:####################################/dev/bNpnxq/.magisk/mirror # ls -altotal 24d---------  7 root   root    220 2022-12-24 21:41 .drwxr-xr-x  8 root   root    180 2022-12-24 21:41 ..drwxrwx--x 50 system system 4096 2022-12-24 21:41 datalrwxrwxrwx  1 root   root      9 2022-12-24 21:41 metadata -> /metadatalrwxrwxrwx  1 root   root     19 2022-12-24 21:41 persist -> /mnt/vendor/persistdrwxr-xr-x 14 root   root   4096 2009-01-01 08:00 productlrwxrwxrwx  1 root   root     17 1970-02-17 10:04 sepolicy.rules -> ./metadata/magisklrwxrwxrwx  1 root   root     20 2022-12-24 21:41 system -> ./system_root/systemdrwxr-xr-x  9 root   root   4096 2009-01-01 08:00 system_extdrwxr-xr-x 26 root   root   4096 2009-01-01 08:00 system_rootdrwxr-xr-x 20 root   shell  4096 2009-01-01 08:00 vendor

配置和模块(/data/adb目录)

不要被目录名迷惑,虽然是adb实际上是Magisk的一个应用目录,这个目录里面保存了Magisk的一些数据和配置文件,安装的模块也是被保存在这里,之所以使用这个目录是有它的优势的:

  • 该目录对于任何出厂设备来说都存在,第三方APP无法据此检测Magisk。
  • 该目录权限默认为700,所有者为root,因此第三方APP无法进入和读写。
  • 该目录的安全上下文secontext是u:object_r:adb_data_file:s0,很少有进程有该权限。
  • 该目录在Device Encrypted (DE) storage,因此当FBE (File-Based Encryption) 设备在Direct Boot mode时或者解锁锁屏后即可使用。

这个目录下包含以下文件(配合Magisk启动流程来看更容易理解):

SECURE_DIR=/data/adb# 存储一些 post-fs-data 后需要执行的脚本的文件夹$SECURE_DIR/post-fs-data.d# 存放通用 late_start 服务脚本的文件夹$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、scripts和 magisk 二进制文件。 用于支持模块安装、addon.d、Magisk应用程序等。DATABIN=$SECURE_DIR/magisk

Magisk启动过程

Magisk启动分为以下几步:Pre-Init,post-fs-data,late_start,Resetprop,这几个启动流程我们可以对应上Android系统启动,下面我详细说明一下:

Pre-Init 阶段

使用 magiskinit替换init并执行:

  • 先挂载所需的分区。 在传统的 system-as-root 设备上,切换root到/system;在2SI 设备上(使用systemless), 将 init 文件重定向到 magiskinit 并执行(原理是我们在patch boot的时候会重定向原来的init步骤 ),以此来挂载所需的分区。
  • 在 init.rc注入magisk服务
  • 对于使用 monolithic (整体的)安全策略的设备,从 /sepolicy读取安全策略;对于其它设备,用FIFO劫持selinuxfs nodes,设置 LD_PRELOAD 来hook security_load_policy ,并且协助劫持2SI 设备,并且启动daemon直到init尝试读取sepolicy。
  • Patch sepolicy 规则。如果使用 “劫持” 方法, 则将修补的sepolicy载入kernel, 然后解除init 劫持并退出daemon。
  • 执行原始的 init 来执行后续的启动过程

post-fs-data阶段

post-fs-data 过程在 /data 解密并挂载后触发。首先会启动守护进程 magiskd ,执行post-fs-data脚本,这个时候模块文件就挂载完毕了。

late_start阶段

在启动过程的后期, late_start 类会被触发,开启Magisk “service” 模式。服务脚本会在这个模式下执行。

到这里Magisk就完全启动了,上面就是App了,他只是负责一些配置的修改和状态显示,以及调用前面启动的服务和程序来执行,大概就是这样的过程。

修改属性(Resetprop)

正常来说,只有init能修改系统属性,非root进程只能读取无法修改。在有root时可以通过由init提供的property_service发送请求(比如adb可以使用setprop命令)来实现修改,但是通过这种方式不能修改和删除只读属性(以ro.开头的属性,例如ro.build.product),除非修改系统源码。

resetprop 通过提取和patch AOSP中和系统属性相关的源代码,从而允许直接修改属性区域(prop_area),不再需要通过property_service来修改系统属性(实际上这个功能和Github上开源的mprop的原理类似)。不过也正是因为绕过了property_service,所以需要有下面的注意事项:

  • 触发事件的修复:由于绕过了property_service,所以当属性改变时,在*.rc脚本中注册的on property:foo=bar 动作事件不会被触发(在init语言中这个叫做属性触发器)。但Mgisk考虑到了这点,所以默认情况下 resetprop设置属性时会和setprop一样,它会触发事件(通过先删除属性再通过property_service来设置属性来实现)。如果不想触发动作事件,可以使用-n参数禁用。

  • 实现重启也保留修改:持久属性(以persist.开头的属性,例如persist.sys.usb.config)在 prop_area 和 /data/property都有存储。默认情况下,删除持久属性时不会把它从持久化存储中移除,也就是说下次重启时持久属性会被恢复; getprop在不会从持久化存储中读取持久属性。但是对于resetprop ,可以使用-p参数,这样删除时会同时将该属性从 prop_area 和 /data/property中移除,读取时也会同时从prop_area 和 持久化存储中读取。

SELinux 策略

Magisk通过patch原来的sepolicy来确保Magisk的操作可以安全地执行。新的magisk域权限很高, magiskd和所有root shell都会在该域中运行。magisk_file是一个新的文件类型,该文件类型允许每一个域访问(不受限制的文件上下文),下面这个magisk文件用的就是magisk_file上下文。

blueline:/data/adb # ls -Zltotal 52drwxr-xr-x 3 root root u:object_r:magisk_file:s0     3488 2022-12-24 21:41 magisk-rw------- 1 root root u:object_r:adb_data_file:s0  40960 2022-12-27 02:22 magisk.dbdrwxr-xr-x 2 root root u:object_r:system_file:s0     3488 2022-12-24 21:41 modulesdrwxr-xr-x 2 root root u:object_r:adb_data_file:s0   3488 2022-12-24 21:41 post-fs-data.ddrwxr-xr-x 2 root root u:object_r:adb_data_file:s0   3488 2022-12-24 21:41 service.d

在 Android 8.0之前,所有被su允许的客户端域都可以直接连接到 magiskd 并且与守护进程连接,从而获得远程的root shell访问。Magisk同时也需要释放一些ioctl 操作以便root shell可以正常工作。

但是,在Android 8.0及以后,为了避免Android沙箱中对规则的放松,Magisk实现了一个新的SELinux模型。 magisk二进制文件被标记为magisk_exec文件类型,进程在以su client的域运行时执行 magisk二进制文件(包括su命令)时会被中转为 magisk_client(通过使用一个type_transition规则)。

规则严格限制了magisk域的进程才被允许归属于magisk_exec文件类型。不再允许直接通过socket连接至magiskd;唯一的访问该守护进程的方式就是通过一个magisk_client 进程。这样做确保了沙箱的完整性,让Magisk专用规则从其它规则中分离。如下magisk64:

blueline:/dev/bNpnxq # ls -lZtotal 720lrwxrwxrwx 1 root    root    u:object_r:system_file:s010 1970-02-17 10:04 magisk -> ./magisk64-rwxr-xr-x 1 root    root    u:object_r:system_file:s0                        154452 1970-02-17 10:04 magisk32-rwxr-xr-x 1 root    root    u:object_r:magisk_exec:s0                        247168 1970-02-17 10:04 magisk64-rwxr-xr-x 1 u0_a206 u0_a206 u:object_r:app_data_file:s0:c206,c256,c512,c768  328240 2022-12-24 21:41 magiskpolicylrwxrwxrwx 1 root    root    u:object_r:system_file:s0 8 1970-02-17 10:04 resetprop -> ./magisklrwxrwxrwx 1 root    root    u:object_r:system_file:s0 8 1970-02-17 10:04 su -> ./magisklrwxrwxrwx 1 root    root    u:object_r:system_file:s014 1970-02-17 10:04 supolicy -> ./magiskpolicy

关于更加详细规则可以在 magiskpolicy/rules.cpp中找到。

来源地址:https://blog.csdn.net/zhonglunshun/article/details/128791807

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Magisk内部实现原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Android中Handler的内部实现原理

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文《详解Android中Handler的使用方法》,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解。 概括来说
2022-06-06

android IntentService实现原理及内部代码分享

很多网友可能发现Android中除了Service还有一个IntentService,他们之间到底有哪些区别呢? 在继承关系上而言IntentService是Service的子类,内部实现的代码中涉及到一些Android入门开发者不
2022-06-06

Ajax的内部实现机制、原理与实践小结

Ajax(Asynchronous JavaScript and XML)是一种在Web应用中实现异步数据交互的技术。它的内部实现机制主要包括以下几个方面:1. 使用XMLHttpRequest对象:Ajax通过XMLHttpRequest
2023-08-15

深入探索:Go WaitGroup的原理和内部实现

WaitGroup是Go语言中的一个并发同步原语,用于等待一组goroutine的完成。它提供了三个主要的方法:Add、Done和Wait。Add方法用于向WaitGroup中添加要等待的goroutine的数量。Done方法用于表示一个g
2023-10-08

夯实Java基础系列18:深入理解Java内部类及其实现原理

本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看https://github.com/h3pl/Java-Tutorial喜欢的话麻烦点下Star哈文章首发于我的个人博客:www.how2pla
2023-06-02

深入理解Golang方法的内部实现

Golang是由Google开发的一种静态类型的编程语言,以其简洁的语法和高效的性能而备受程序员欢迎。在Golang中,方法是一种特殊的函数,用于为结构体添加行为。本文将深入探讨Golang方法的内部实现,通过具体的代码示例帮助读者更好地理
深入理解Golang方法的内部实现
2024-02-23

linux内核锁的实现原理是什么

Linux内核锁的实现原理是通过硬件的原子操作指令或者特殊的指令序列来保证对共享资源的原子操作,从而实现线程之间的同步和互斥。Linux内核提供了多种锁机制,包括自旋锁、互斥锁、读写锁等。自旋锁是一种忙等待的锁,当一个线程尝试获取自旋锁失败
2023-10-21

android底部菜单栏实现原理与代码

上一个项目已经做完了,这周基本上没事,所以整理了下以前的项目,想把一些通用的部分封装起来,这样以后遇到相似的项目就不用重复发明轮子了,也节省了开发效率。今天把demo贴出来一是方便以后自己查询,二是希望同时也能帮到大家。 底部菜单栏很重要,
2022-06-06

揭秘 Java 文件操作的内部原理

Java 文件操作是 Java 开发中不可或缺的一部分,了解其内部原理有助于开发者更好地理解和使用 Java 文件操作 API。
揭秘 Java 文件操作的内部原理
2024-02-26

Oracle的内部结构及SQL优化原理

Oracle结构;         主要包括两部分:1.Oracle实例 2.数据库文件 Orac le 实例: 1.是访问 Oracle database 的途径 2.只能打开一个数据库 3.由 SGA 内存区和一组后台进程组成 Oracle数据库文件:Or
Oracle的内部结构及SQL优化原理
2021-02-08

Linq内部执行原理是怎么样的

这篇文章主要介绍了Linq内部执行原理是怎么样的,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Linq内部执行原理LINQ(Language Integrated Query
2023-06-17

HBase的存储引擎内部原理剖析

HBase是一个分布式、可扩展、面向列的NoSQL数据库,它建立在Hadoop分布式文件系统(HDFS)之上,用于处理大规模数据集。以下是HBase存储引擎内部原理的剖析:HBase存储引擎内部原理数据模型:HBase的数据模型是一个稀疏
HBase的存储引擎内部原理剖析
2024-10-19

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录