
Root an Android Studio emulator by patching its ramdisk with Magisk — in pure Go.
Root an Android Studio emulator by patching its ramdisk with Magisk — in pure Go.
avdroot takes an AVD from a cold start to a working root shell with one
command. Everything happens on the host: the ramdisk is decompressed, rewritten
so Magisk's init replaces /init, and recompressed. Building the image needs no
shell script, no busybox, no magiskboot, and no code execution inside the
emulator.
$ avdroot root
==> Target
Pixel_10_Pro_XL (the only AVD)
==> Preparing Magisk's environment
Magisk's files are not unpacked yet; writing them for 31.0 now
ok 14 files written for Magisk 31.0, before the restart that will use them
==> Patching
preinit device: vdd1 (read from the running emulator)
==> Restarting the emulator
booting cold, because a snapshot would restore the pre-patch ramdisk
==> Installing the Magisk app
ok installed com.topjohnwu.magisk (Magisk 31.0)
==> Granting su to the adb shell
ok uid 2000 allowed
==> Verifying root
ok Magisk 31.0:MAGISK:R is running
ok su grants root
root: uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0
rootAVD pioneered this and is still the reference for the technique, but it has not kept up with recent Android and Magisk releases. Running it against an Android 17 AVD shows the problems plainly, and each one is fixed here:
| rootAVD behaviour | avdroot |
|---|---|
Requires bash, busybox, unzip, xxd, strings, dd, cpio, and GNU stat/sed (which differ on macOS) | One static Go binary. cpio, LZ4, gzip and xz are all implemented in-process |
| Copies the script plus binaries into the emulator and runs itself there | Builds the image entirely on the host |
Backs up ramdisk.img on every run, so after the first patch the "pristine" backup is itself patched and restore cannot return to stock | Backs up once, never overwrites, and warns if an existing backup turns out to be patched |
Patches for Magisk's old magisk32/magisk64 layout | Implements Magisk 26+ (magiskinit, magisk, init-ld, .backup/.rmlist) |
| Needs the ramdisk path pasted as an argument | Discovers the SDK, AVDs and images, with --sdk/--avd-home to override |
| Unconditionally reboots and hopes | Forces a cold boot and verifies that su actually returns uid=0 |
Prebuilt binaries — take the archive for your platform from Releases:
tar -xzf avdroot_linux_amd64.tar.gz
chmod +x avdroot_linux_amd64
sudo mv avdroot_linux_amd64 /usr/local/bin/avdroot
Windows and macOS archives contain a single executable; on macOS you may need to clear the quarantine attribute:
xattr -d com.apple.quarantine avdroot_darwin_arm64
Every release also publishes SHA256SUMS.txt:
sha256sum -c SHA256SUMS.txt --ignore-missing
With Go (1.24 or newer):
go install github.com/ejfkdev/avdroot@latest
Go 1.24 is the floor because earlier toolchains emit no LC_UUID load command,
and macOS 26's dyld refuses to launch such a binary. Go 1.21 and 1.22 stamp
minos 26.0 as well, so they fail on both counts.
From source:
git clone https://github.com/ejfkdev/avdroot
cd avdroot
make build # host binary
make all # every release target into dist/
You also need an Android SDK with adb and the emulator, and a google_apis or
AOSP system image. Play Store images cannot be rooted this way: they refuse
adb root, so patch and root reject them immediately rather than failing
several minutes later.
avdroot list # what exists, and what state it is in
avdroot root # patch, restart, install the app, grant su, verify
That is the whole thing. root needs no AVD name when only one exists, asks for
confirmation once, and skips the question entirely when stdin is not a terminal:
avdroot root Pixel_10_Pro_XL --yes # named AVD, unattended
If you would rather do it in steps:
avdroot patch # patch only; the emulator need not be running
avdroot patch --dry-run # show what would change, write nothing
# restart the emulator yourself, then
avdroot install && avdroot verify
To undo:
avdroot restore # restores ramdisk.img.backup
| Command | Description |
|---|---|
avdroot root [target] | Patch, restart, install, grant su, verify — unattended |
avdroot patch [target] | Patch the ramdisk only; asks before writing |
avdroot restore [target] | Restore from ramdisk.img.backup |
avdroot list | AVDs, system images and their patch status |
avdroot status [target] | Details for one target, including the preinit device |
avdroot verify [target] | Check that Magisk runs and su returns uid=0 |
avdroot install | Install the Magisk app into the running emulator |
avdroot magisk info | Which installer would be used, and what it supports |
avdroot magisk fetch | Download a release (--list, --version, --prerelease) |
avdroot trust-chrome | Make Chrome accept an interception CA (--cert, --clear) |
avdroot doctor | Explain how every path was located |
A target is an AVD name, a system-image path, or a path to a ramdisk.img.
avdroot <command> --help shows examples for each.
These apply to every command: