Skip to content

Commit

Permalink
Update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
topjohnwu committed Oct 3, 2020
1 parent 716f068 commit fbb4f85
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 218 deletions.
2 changes: 1 addition & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Here are some feature highlights:
## Useful Links

- [Installation Instruction](https://topjohnwu.github.io/Magisk/install.html)
- [OTA Upgrade Guide](https://topjohnwu.github.io/Magisk/ota.html)
- [Frequently Asked Questions](https://topjohnwu.github.io/Magisk/faq.html)
- [Full Official Docs](https://topjohnwu.github.io/Magisk/)
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))

Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Magisk Documentation
(Updated on 2020.3.23)
(Updated on 2020.10.2)

- [Installation Instructions](install.md)
- [OTA Upgrade Guides](ota.md)
- [Frequently Asked Questions](faq.md)

The following sections are for developers

Expand Down
20 changes: 10 additions & 10 deletions docs/boot.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ Method | Initial rootdir | Final rootdir
- Devices that does not fall in any of Method B and C's criteria
- **Method B - Legacy SAR**: This method was first seen on Pixel 1. The kernel directly mounts the `system` partition as rootdir and exec `/init` to boot.
- Devices with `(LV = 28)`
- Google: Pixel 1 and 2. Pixel 3 and 3a with `(RV = 28)` only.
- OnePlus: 5T - 7
- Google: Pixel 1 and 2. Pixel 3 and 3a when `(RV = 28)`.
- OnePlus: 6 - 7
- Maybe some `(LV < 29)` Android Go devices?
- **Method C - 2SI ramdisk SAR**: This method was first seen on Pixel 3 Android 10 developer preview. The kernel uses `initramfs` as rootdir and exec `/init` in `rootfs`. This `init` is responsible to mount the `system` partition and use it as the new rootdir, then finally exec `/system/bin/init` to boot.
- Devices with `(LV >= 29)`
- Devices with `(LV < 28, RV >= 29)`, excluding exceptions that were using Method B
- Devices with `(LV < 28, RV >= 29)`, excluding those that were already using Method B
- Google: Pixel 3 and 3a with `(RV >= 29)`

### Discussion
Expand All @@ -47,7 +47,7 @@ However for Magisk, the real difference lies in what the device ends up using wh
The criteria for Method C is a little complicated, in layman's words: either your device is modern enough to launch with Android 10+, or you are running an Android 10+ custom ROM on a device that was using Method A.

- Any Method A device running Android 10+ will automatically be using Method C
- **Method B devices are stuck with Method B**, maybe only with the exception of Pixel 3 and 3a, which Google retrofitted the device to adapt the new method.
- **Method B devices are stuck with Method B**, with the only exception being Pixel 3 and 3a, which Google retrofitted the device to adapt the new method.

SAR is a very important part of [Project Treble](https://source.android.com/devices/architecture#hidl) as rootdir should be tied to the platform. This is also the reason why Method B and C comes with `(LV >= ver)` criterion as Google has enforced all OEMs to comply with updated requirements every year.

Expand All @@ -57,7 +57,9 @@ When Google released the first generation Pixel, it also introduced [A/B (Seamle

Let's go back in time when Google is first designing A/B. If using SAR (only Boot Method B exists at that time), the kernel doesn't need `initramfs` to boot Android (because rootdir is in `system`). This mean we can be smart and just stuff the recovery ramdisk (containing the minimalist Linux environment) into `boot`, remove `recovery`, and let the kernel pick whichever rootdir to use (ramdisk or `system`) based on information from the bootloader.

As time passed from Android 7.1 to Android 10, Google introduced [Dynamic Partitions](https://source.android.com/devices/tech/ota/dynamic_partitions/implement). This is bad news for SAR, because the Linux kernel cannot understand this new partition format, thus unable to directly use `system` as rootdir. This is when they came up with Boot Method C: always boot into `initramfs`, and let userspace handle the rest of booting.
As time passed from Android 7.1 to Android 10, Google introduced [Dynamic Partitions](https://source.android.com/devices/tech/ota/dynamic_partitions/implement). This is bad news for SAR, because the Linux kernel cannot directly understand this new partition format, thus unable to directly mount `system` as rootdir. This is when they came up with Boot Method C: always boot into `initramfs`, and let userspace handle the rest of booting. This includes deciding whether to boot into Android or recovery, or as they officially call: `USES_RECOVERY_AS_BOOT`.

Some modern devices using A/B with 2SI also comes with `recovery_a/_b` partitions. This is officially supported with Google's standard. These devices will then only use the boot ramdisk to boot into Android as recovery is stored on a separate partition.

## Piecing Things Together

Expand All @@ -66,19 +68,17 @@ With all the knowledge above, now we can categorize all Android devices into the
Type | Boot Method | Partition | 2SI | Ramdisk in `boot`
:---: | :---: | :---: | :---: | :---:
**I** | A | A-only | No | `boot` ramdisk
**II** | B | A/B | No | `recovery` ramdisk
**III** | B | A-only | No | ***N/A***
**II** | B | A/B | Any | `recovery` ramdisk
**III** | B | A-only | Any | ***N/A***
**IV** | C | Any | Yes | Hybrid ramdisk
**II\* / III\*** | B | II / III | Yes | II / III

These types are ordered chronologically by the time they were first available.

- **Type I**: Good old legacy ramdisk boot
- **Type II**: Legacy A/B devices. Pixel 1 is the first device of this type, being both the first A/B and SAR device
- **Type III**: Late 2018 - 2019 devices that are A-only. **The worst type of device to ever exist as far as Magisk is concerned.**
- **Type IV**: All devices using Boot Method C are Type IV. A/B Type IV ramdisk can boot into either Android or recovery based on info from bootloader; A-only Type IV ramdisk can only boot into Android.
- **Type \***: Type II* and III* are Boot Method B devices running on Android 10+ (2SI). **They are NOT new device types**. In Magisk the code treat them drastically different due to complicated reasons, hence mentioning them here.

Further details on Type III devices: Magisk is always installed in the ramdisk of a boot image. For all other device types, because their `boot` partition have ramdisk included, Magisk can be easily installed by patching boot image through Magisk Manager or flash zip in custom recovery. However for Type III devices, they are **limited to install Magisk into the `recovery` partition**. Magisk will not function when booted normally; instead Type III device owners have to always reboot to recovery to maintain Magisk access.

Some Type III devices' bootloader will still accept and provide `initramfs` manually added to `boot` image to the kernel (e.g. some Xiaomi phones), but many device don't (e.g. Samsung S10, Note 10). It solely depends on how the OEM implements its bootloader.
Some Type III devices' bootloader will still accept and provide `initramfs` that was manually added to the `boot` image to the kernel (e.g. some Xiaomi phones), but many device don't (e.g. Samsung S10, Note 10). It solely depends on how the OEM implements its bootloader.
2 changes: 1 addition & 1 deletion docs/deploy.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Deployment
(Note: This is not a user tutorial for installing Magisk, this is an explaination of how Magisk can be installed, and a guide for developers to properly deploy Magisk in various different situations)
(Note: This is not a user tutorial for installing Magisk, this is an explanation of how Magisk can be installed, and a guide for developers to properly deploy Magisk in various different situations)

## Systemless
When a user flashes a Magisk zip in custom recoveries or have boot images patched in Magisk Manager, Magisk is installed in the systemless fashion. This is the only officially supported method to install Magisk on a device. The systemless method installs Magisk into a boot image's ramdisk CPIO, sometimes require additional patches to the kernel.
Expand Down
91 changes: 56 additions & 35 deletions docs/details.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,54 @@
# Internal Details

## File Structure
### Paths in "sbin tmpfs overlay"
One of Magisk's breakthrough designs is sbin tmpfs overlay. It is required to support system-as-root devices, and also is the key to hiding Magisk from detection. All Magisk binaries, applets, mirrors, and other trivial stuffs are all located in the `tmpfs` mounted on `/sbin`. MagiskHide can just simply unmount `/sbin` and the bind mounts to hide all modifications easily.

### Paths in "Magisk tmpfs directory"

Magisk will mount a `tmpfs` directory to store some temporary data. For devices with the `/sbin` folder, it will be chosen as it will also act as an overlay to inject binaries into `PATH`. From Android 11 onwards, the `/sbin` folder might not exist, so Magisk will randomly create a folder under `/dev` and use it as the base folder.

```
# In order to get the current base folder Magisk is using,
# use the command `magisk --path`.
# Binaries like magisk, magiskinit, and all symlinks to
# applets are directly stored in /sbin, so they
# are all in PATH for apps and shell to access them
# applets are directly stored in this path. This means when
# this is /sbin, these binaries will be directly in PATH.
MAGISKPATH=$(magisk --path)
# Magisk internal stuffs
MAGISKTMP=/sbin/.magisk
# Magisk BusyBox path
BBPATH=$MAGISKTMP/busybox
# /data/adb/modules will be bind mounted here
MAGISKTMP=$MAGISKBASE/.magisk
# Magisk's BusyBox directory. Within this folder stores
# the busybox binary and symlinks to all of its applets.
# Any usage of this directory is deprecated, please
# directly call /data/adb/magisk/busybox and use
# BusyBox's ASH Standalone mode.
# The creation of this path will be removed in the future.
$MAGISKTMP/busybox
# /data/adb/modules will be bind mounted here.
# The original folder is not used due to nosuid mount flag.
$MAGISKTMP/modules
# The configuration used in last installation
# The current Magisk installation config
$MAGISKTMP/config
# Partition mirrors.
# There would be system, vendor, data, and possibly product
# in this directory, each is the mirror to the name of the partition
# Partition mirrors
# Each directory in this path will be mounted with the
# partition of its directory name.
# e.g. system, system_ext, vendor, data ...
$MAGISKTMP/mirror
# Block devices Magisk creates internally to mount mirrors.
$MAGISKTMP/block
# Root directory patch files
# On system-as-root devices, / is not writable.
# All patched files are stored here and bind mounted at boot.
# All pre-init patched files are stored here and bind mounted.
$MAGISKTMP/rootdir
# The patched sepolicy file on system-as-root devices.
# This is required as /sepolicy does not exist
# on those devices and / is not writable.
/sbin/.se
```

### Paths in `/data`

Some binaries and files should be stored on non-volatile storages in `/data`. In order to prevent detection, everything has to be stored somewhere safe and undetectable in `/data`. The folder `/data/adb` was chosen because of the following advantages:

- It is an existing folder on modern Android, so it cannot be used as an indication of the existence of Magisk.
Expand Down Expand Up @@ -66,31 +77,36 @@ $SECURE_DIR/modules_update
# Database storing settings and root permissions
MAGISKDB=$SECURE_DIR/magisk.db
# All magisk related binaries, containing busybox,
# All magisk related binaries, including busybox,
# scripts, and magisk binaries. Used in supporting
# module installation, addon.d, Magisk Manager etc.
# This folder will be bind mounted to $BINMIRROR
DATABIN=$SECURE_DIR/magisk
```

## Magisk Booting Process

### Pre-Init

`magiskinit` will replace `init` as the first program to run.

- Early mount required partitions. On system-as-root devices, we will switch root to system
- Inject magisk services into `init.rc`
- Early mount required partitions. On legacy system-as-root devices, we switch root to system; on 2SI devices, we patch fstab and execute the original `init` to mount partitions for us.
- Load sepolicy either from `/sepolicy`, precompiled sepolicy in vendor, or compile split sepolicy
- Patch sepolicy rules and dump to `/sepolicy` or `/sbin/.se` and patch `init` or `libselinux.so` to load the patched policies
- Execute the original `init` to start the ordinary boot process
- Patch sepolicy rules and dump to `/sepolicy` or `/sbin/.se` or `/dev/.se`
- Patch `init` or `libselinux.so` to force the system to load the patched policies
- Inject magisk services into `init.rc`
- Execute the original `init` to continue the boot process

### post-fs-data
This triggers on `post-fs-data` when `/data` is properly decrypted (if required) and mounted. The daemon `magiskd` will be launched, post-fs-data scripts are executed, and module files are magic mounted.

This triggers on `post-fs-data` when `/data` is decrypted and mounted. The daemon `magiskd` will be launched, post-fs-data scripts are executed, and module files are magic mounted.

### late_start
Later in the booting process, the class `late_start` will be triggered, and Magisk "service" mode will be started. In this mode, service scripts are executed, and it will try to install Magisk Manager if it doesn't exist.

Later in the booting process, the class `late_start` will be triggered, and Magisk "service" mode will be started. In this mode, service scripts are executed.

## Resetprop

Usually, system properties are designed to only be updated by `init` and read-only to non-root processes. With root you can change properties by sending requests to `property_service` (hosted by `init`) using commands such as `setprop`, but changing read-only props (props that start with `ro.` like `ro.build.product`) and deleting properties are still prohibited.

`resetprop` is implemented by distilling out the source code related to system properties from AOSP and patched to allow direct modification to property area, or `prop_area`, bypassing the need to go through `property_service`. Since we are bypassing `property_service`, there are a few caveats:
Expand All @@ -99,16 +115,21 @@ Usually, system properties are designed to only be updated by `init` and read-on
- persist properties (props that starts with `persist.`, like `persist.sys.usb.config`) are stored in both `prop_area` and `/data/property`. By default, deleting props will **NOT** remove it from persistent storage, meaning the property will be restored after the next reboot; reading props will **NOT** read from persistent storage, as this is the behavior of `getprop`. With the flag `-p`, deleting props will remove the prop in **BOTH** `prop_area` and `/data/property`, and reading props will be read from **BOTH** `prop_area` and persistent storage.

## Magic Mount
I will skip the details in the actual implementation and algorithm of Magic Mount, but you can always directly dive into the source code if interested. (`bootstages.cpp`)

Even though the mounting logic is pretty complicated, the final result of Magic Mount is actually pretty simple. For each module, the folder `$MODPATH/system` will be recursively merged into the real `/system`; that is: existing files in the real system will be replaced by the one in modules' system, and new files in modules' system will be added to the real system.
The details of the actual implementation and algorithm of Magic Mount is omitted here, please directly dive into the source code if interested (`core/module.cpp`).

Even though the mounting logic is very complicated, the final result of Magic Mount is actually pretty simple. For each module, the folder `$MODPATH/system` will be recursively merged into the real `/system`; that is: existing files in the real system will be replaced by the one in modules' system, and new files in modules' system will be added to the real system.

There is one additional trick you can use: if you place an empty file named `.replace` in any of the folders in a module's system, instead of merging the contents, that folder will directly replace the one in the real system. This will be very handy in some cases, for example swapping out a system app.

If you want to replace files in `/vendor` or `/product`, please place them under `$MODPATH/system/vendor` or `$MODPATH/system/product`. Magisk will transparently handle both cases, whether vendor or product is a separate partition or not.

## Miscellaneous
Here are some tidbits in Magisk but unable to be categorized into any sections:
## SELinux Policies

Magisk will patch the stock `sepolicy` to make sure root and Magisk operations can be done in a safe and secure way. The new domain `magisk` is effectively permissive, which is what `magiskd` and all root shell will run in. `magisk_file` is a new file type that is setup to be allowed to be accessed by every domain (unrestricted file context).

Before Android 8.0, all allowed su client domains are allowed to directly connect to `magiskd` and establish connection with the daemon to get a remote root shell. Magisk also have to relax some `ioctl` operations so root shells can function properly.

After Android 8.0, to reduce relaxation of rules in Android's sandbox, a new SELinux model is deployed. The `magisk` binary is labelled with `magisk_exec` file type, and processes running as allowed su client domains executing the `magisk` binary (this includes the `su` command) will transit to `magisk_client` by using a `type_transition` rule. Rules strictly restrict that only `magisk` domain processes are allowed to attribute files to `magisk_exec`. Direct connection to sockets of `magiskd` are not allowed; the only way to access the daemon is through a `magisk_client` process. These changes allow us to keep the sandbox intact, and keep Magisk specific rules separated from the rest of the policies.

- Socket name randomization: when you call `su`, `magiskhide`, and some commands in `magisk`, it connects to the magisk daemon `magiskd` running in the background. The connections are established through an abstract Unix socket. Any process can go through all active Unix sockets and see if the specifc name used by Magisk is in the list to determine whether `magiskd` is running. Starting from v15.4, the abstract name used in `magiskd` and `magisklogd` are randomized by `magiskinit` on each boot.
- Sevice name randomization: each service started up by `init` will be recorded. Some apps will detect the name of magisk boot services to determine whether Magisk is installed. Starting from v17.2, the service name assigned in `init.magisk.rc` is randomized by `magiskinit`.
The full set of rules can be found in `magiskpolicy/rules.cpp`.
11 changes: 11 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Frequently Asked Questions

### Q: Why is X app detecting root?

Manually enable MagiskHide in settings (MagiskHide is no longer enabled by default). Also, there are known methods to detect Magisk, so your mileage may vary.

### Q: I installed a module and it bootlooped my device. Help!

If you have USB debugging enabled in developer options, connect your phone to the PC. If your device is detected (check by `adb devices`), enter ADB shell and run the command `magisk --remove-modules`. This will remove all your modules and automatically reboot the device.

If unfortunately you do not have USB debugging enabled, reboot into Safe Mode. Most modern Android devices support pressing a special key combo at boot to enter Safe Mode as an emergency option. Magisk will detect Safe Mode being activated, and all modules will be disabled. Then reboot back to normal mode (the module disable state persists) and manage your modules through Magisk Manager.
Loading

0 comments on commit fbb4f85

Please sign in to comment.