mirror of https://github.com/topjohnwu/magisk
114 lines
6.9 KiB
Markdown
114 lines
6.9 KiB
Markdown
# Internal Details
|
|
|
|
## File Structure
|
|
|
|
### 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 use `/debug_ramdisk` 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 this path. This means when
|
|
# this is /sbin, these binaries will be directly in PATH.
|
|
MAGISKTMP=$(magisk --path)
|
|
|
|
# Magisk internal stuffs
|
|
INTERNALDIR=$MAGISKTMP/.magisk
|
|
|
|
# /data/adb/modules will be bind mounted here.
|
|
# The original folder is not used due to nosuid mount flag.
|
|
$INTERNALDIR/modules
|
|
|
|
# The current Magisk installation config
|
|
$INTERNALDIR/config
|
|
|
|
# Partition mirrors
|
|
# Each directory in this path will be mounted with the
|
|
# partition of its directory name.
|
|
# e.g. system, system_ext, vendor, data ...
|
|
$INTERNALDIR/mirror
|
|
|
|
# Root directory patch files
|
|
# On system-as-root devices, / is not writable.
|
|
# All pre-init patched files are stored here and bind mounted.
|
|
$INTERNALDIR/rootdir
|
|
```
|
|
|
|
### 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.
|
|
- The permission of the folder is by default `700`, owner as `root`, so non-root processes are unable to enter, read, write the folder in any possible way.
|
|
- The folder is labeled with secontext `u:object_r:adb_data_file:s0`, and very few processes have the permission to do any interaction with that secontext.
|
|
- The folder is located in _Device encrypted storage_, so it is accessible as soon as data is properly mounted in FBE (File-Based Encryption) devices.
|
|
|
|
```
|
|
SECURE_DIR=/data/adb
|
|
|
|
# Folder storing general post-fs-data scripts
|
|
$SECURE_DIR/post-fs-data.d
|
|
|
|
# Folder storing general late_start service scripts
|
|
$SECURE_DIR/service.d
|
|
|
|
# Magisk modules
|
|
$SECURE_DIR/modules
|
|
|
|
# Magisk modules that are pending for upgrade
|
|
# Module files are not safe to be modified when mounted
|
|
# Modules installed through the Magisk app will be stored here
|
|
# and will be merged into $SECURE_DIR/modules in the next reboot
|
|
$SECURE_DIR/modules_update
|
|
|
|
# Database storing settings and root permissions
|
|
MAGISKDB=$SECURE_DIR/magisk.db
|
|
|
|
# All magisk related binaries, including busybox,
|
|
# scripts, and magisk binaries. Used in supporting
|
|
# module installation, addon.d, the Magisk app etc.
|
|
DATABIN=$SECURE_DIR/magisk
|
|
|
|
```
|
|
|
|
## Magisk Booting Process
|
|
|
|
### Pre-Init
|
|
|
|
`magiskinit` will replace `init` as the first program to run.
|
|
|
|
- Early mount required partitions. On legacy system-as-root devices, we switch root to system; on 2SI devices, we patch the original `init` to redirect the 2nd stage init file to magiskinit and execute it to mount partitions for us.
|
|
- Inject magisk services into `init.rc`
|
|
- On devices using monolithic policy, load sepolicy from `/sepolicy`; otherwise we hijack nodes in selinuxfs with FIFO, set `LD_PRELOAD` to hook `security_load_policy` and assist hijacking on 2SI devices, and start a daemon to wait until init tries to load sepolicy.
|
|
- Patch sepolicy rules. If we are using "hijack" method, load patched sepolicy into kernel, unblock init and exit daemon
|
|
- Execute the original `init` to continue the boot process
|
|
|
|
### post-fs-data
|
|
|
|
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.
|
|
|
|
## 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:
|
|
|
|
- `on property:foo=bar` actions registered in `*.rc` scripts will not be triggered if property changes does not go through `property_service`. The default set property behavior of `resetprop` matches `setprop`, which **WILL** trigger events (implemented by first deleting the property then set it via `property_service`). There is a flag `-n` to disable it if you need this special behavior.
|
|
- 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.
|
|
|
|
## 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.
|
|
|
|
The full set of rules can be found in `sepolicy/rules.cpp`.
|