How Magisk Two-Stage Init Works on Modern Android Devices
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). 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 (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_initramfsis true, it triggers legacy system-as-root mode - If
config.force_normal_bootis 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. 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:
- Creates a symlink at
/storage/self/primarypointing to/system/system/bin/init(or the equivalent path inside the first-stage ramdisk whenforce_normal_bootis enabled) - Renames the original
/initexecutable to/sdcard - 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:
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 script automates the preparation of boot images for the two-stage init process. You can manually replicate these steps using the magiskboot utility:
# 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) determines whether the device uses the two-stage flow during runtime execution.
Summary
- MagiskInit::start in
native/src/init/init.rsserves 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.rsmanipulates 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_bootpartitions.
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →