# How Magisk Two-Stage Init Works on Modern Android Devices

> Discover how Magisk two-stage init on Android intercepts the boot process. Learn about SwitchRoot hijacking and hex-patching for seamless root access.

- Repository: [John Wu/Magisk](https://github.com/topjohnwu/Magisk)
- Tags: internals
- Published: 2026-03-05

---

**Magisk's two-stage init process works by replacing the stock Android init binary with magiskinit, which intercepts the boot sequence as PID 1, manipulates the filesystem through SwitchRoot hijacking or hex-patching techniques, and then transfers execution to the real second-stage init while maintaining root capabilities.**

Magisk's two-stage init mechanism is the foundation of its systemless root implementation on modern Android devices. According to the topjohnwu/Magisk source code, this process allows the framework to intercept the boot sequence early without permanently modifying the stock init binary. The implementation supports Android 10 (API 29) through Android 13 (API 35), including devices using the Generic Kernel Image (GKI) with `init_boot` partitions.

## First-Stage Init Entry Point and Decision Logic

When the kernel starts `/init`, the binary is actually **magiskinit** (the Rust and C++ binary located in [`native/src/init/init.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/init.rs)). The `MagiskInit::start` function parses the boot command line, sets up `/proc` and `/sys`, loads the Magisk configuration, and determines which boot path to take.

In [`native/src/init/init.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/init.rs) (lines 1-65), the decision logic evaluates several conditions:

- If `argv[1] == "selinux_setup"`, the system enters **second-stage init** (original init is already running)
- If `config.skip_initramfs` is true, it triggers **legacy system-as-root** mode
- If `config.force_normal_boot` is true, it forces **first-stage init**
- If the device is in recovery or charger mode, it follows the **recovery/charger** path
- If `check_two_stage()` returns true, it enters **first-stage init**
- Otherwise, it falls back to **rootfs init** (no two-stage)

This branching logic ensures Magisk adapts to various device configurations while maintaining compatibility with modern Android's modular architecture.

## The SwitchRoot Hijack Mechanism

The primary two-stage implementation resides in `MagiskInit::first_stage`, defined in [`native/src/init/twostage.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/twostage.rs). This method executes when `check_two_stage()` confirms the device uses the modern init flow.

The `hijack_init_with_switch_root` function (lines 44-88 in twostage.rs) performs the following sequence:

1. **Creates a symlink** at `/storage/self/primary` pointing to `/system/system/bin/init` (or the equivalent path inside the first-stage ramdisk when `force_normal_boot` is enabled)
2. **Renames** the original `/init` executable to `/sdcard`
3. **Bind-mounts** the magiskinit binary (from rootfs or `/data/magiskinit`) onto `/sdcard`

When the original init process later executes its **SwitchRoot** operation—which moves all mounts under `/` into `/system` and performs a chroot there—the symlink causes magiskinit to be bind-mounted over the real `/system/bin/init`. The original init continues execution, but now the binary at `/system/bin/init` is the magiskinit copy, effectively hijacking the second stage while appearing to run the genuine init binary.

## Hex-Patch Fallback for A/B Devices

When the SwitchRoot trick cannot be used—specifically when `/sdcard` already exists, which occurs on certain A/B partition devices—Magisk falls back to binary patching. The `hexpatch_init_for_second_stage` function (lines 6-28 in twostage.rs) opens the original `/init` file as a read-only memory map, locates the byte pattern `"system/bin/init"`, and replaces it with the path to Magisk's magiskinit binary (`/data/magiskinit`).

The implementation either writes the modified file back to disk or bind-mounts a replacement from `/data/init`. This fallback ensures two-stage compatibility across diverse device implementations without requiring specific partition layouts.

## Second-Stage Init Execution and Cleanup

Once the hijacked init executes (now running as the original `/system/bin/init`), `MagiskInit::second_stage` takes control. This function performs critical cleanup operations:

```rust
fn second_stage(&mut self) {
    info!("Second Stage Init");
    cstr!("/init").unmount().ok();                // clear the placeholder
    cstr!("/system/bin/init").unmount().ok();     // safety
    cstr!("/data/init").remove().ok();

    // Tell the rest of the system to run the real init binary
    unsafe { *self.argv = raw_cstr!("/system/bin/init") as *mut _; }
    
    // Handle rootfs devices if necessary
    if is_rootfs() { /* ... */ }
}

```

The function unmounts temporary overlay files, removes the patched `/data/init` if present, and restores `argv[0]` to point to the genuine `/system/bin/init`. For devices that still boot from a read-only rootfs (such as certain Meizu devices), Magisk creates symlinks or patches the rootfs directly using `patch_rw_root` or `patch_ro_root` before transferring control.

## Preparing Boot Images with magiskboot

The [`scripts/boot_patch.sh`](https://github.com/topjohnwu/Magisk/blob/main/scripts/boot_patch.sh) script automates the preparation of boot images for the two-stage init process. You can manually replicate these steps using the `magiskboot` utility:

```bash

# Extract the ramdisk from a stock boot.img

magiskboot unpack boot.img

# Apply the two-stage init patch (magiskboot detects the device ABI)

magiskboot cpio ramdisk.cpio add 0750 init magiskinit
magiskboot cpio ramdisk.cpio add 0644 overlay.d/sbin/init-ld.xz init-ld.xz

# Re-pack and sign the image

magiskboot repack boot.img

```

These commands insert the magiskinit binary into the ramdisk with appropriate permissions (0750) and add the required loader components. The `mount_system_root()` helper (exposed via the CXX bridge in [`native/src/init/lib.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/lib.rs)) determines whether the device uses the two-stage flow during runtime execution.

## Summary

- **MagiskInit::start** in [`native/src/init/init.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/init.rs) serves as the entry point that determines whether to execute first-stage or second-stage initialization based on kernel arguments and device state.
- **SwitchRoot hijacking** in [`native/src/init/twostage.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/init/twostage.rs) manipulates filesystem mounts and symlinks to replace the real init binary with magiskinit during the switch_root operation.
- **Hex-patching fallback** provides an alternative injection method for devices where the SwitchRoot trick is incompatible, modifying the init binary in memory to redirect execution.
- **Second-stage cleanup** removes temporary mounts and restores the original init path in `argv[0]` before handing control to the genuine Android init process.
- The process supports modern Android versions from API 29 through API 35, including GKI 2.0 devices with `init_boot` partitions.

## Frequently Asked Questions

### What is two-stage init in Android?

Two-stage init refers to Android's modern boot architecture introduced in Android 10, where the initial boot process splits into a first stage (which prepares the system and switches root) and a second stage (which runs the actual init process from the system partition). This separation allows for more modular system updates and improved security boundaries between the boot loader and the running system.

### How does Magisk avoid permanent modification of the init binary?

Magisk employs temporary bind-mounts and memory patching rather than persistent writes to the system partition. During first-stage init, it either uses the SwitchRoot technique to overlay magiskinit via bind-mounts or patches a copy of the init binary in `/data`. When second-stage init completes its setup, it unmounts these overlays and removes temporary files, leaving the original `/system/bin/init` untouched on disk while still achieving code execution during the boot process.

### Which devices are compatible with Magisk's two-stage init?

The implementation supports Android 10 through Android 13 (API 29-35), including devices using Generic Kernel Image (GKI) 2.0 with dedicated `init_boot` partitions. The `check_two_stage()` function specifically looks for the `init_boot` partition or pre-init ramdisk capabilities to determine compatibility. Devices using legacy system-as-root configurations without two-stage support fall back to the rootfs init path instead.

### What happens if the SwitchRoot hijack fails during boot?

If the SwitchRoot hijack cannot execute—typically because `/sdcard` already exists on certain A/B partition layouts—Magisk automatically falls back to the hex-patch method. This alternative approach modifies the init binary's string references to point to `/data/magiskinit` instead of the standard system path. If both methods fail, the device may boot without root access or enter a bootloop, though Magisk's safety mechanisms typically prevent permanent device damage by falling back to normal boot when possible.