avdroot 用一条命令把模拟器从冷启动状态带到可用的 root shell。全部工作在主机侧完成:
解压 ramdisk、改写让 Magisk 的 init 替换 /init、再重新打包。构建镜像不需要 shell
脚本、busybox、magiskboot,也不需要在模拟器里执行任何代码。
$ 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 开创了这套做法,至今仍是该技术的参考实现, 但它没能跟上近年的 Android 和 Magisk 版本。拿它跑一个 Android 17 的 AVD,问题会直接 暴露出来,而这些问题在这里都修掉了:
| rootAVD 的行为 | avdroot |
|---|---|
需要 bash、busybox、unzip、xxd、strings、dd、cpio,以及 GNU 版的 stat/sed(在 macOS 上行为不同) | 单个静态 Go 二进制。cpio、LZ4、gzip、xz 全部在进程内实现 |
| 把脚本和二进制推进模拟器,在里面再跑一遍自己 | 完全在主机侧构建镜像 |
每次运行都备份 ramdisk.img,于是打过一次补丁后「原始备份」本身就是补丁版,restore 再也回不到原厂 | 只备份一次,永不覆盖;发现已有备份本身是补丁版会明确警告 |
按 Magisk 旧的 magisk32/magisk64 布局打补丁 | 实现 Magisk 26+(magiskinit、magisk、init-ld、.backup/.rmlist) |
| 需要手动粘贴 ramdisk 路径 | 自动识别 SDK、AVD 和镜像,可用 --sdk/--avd-home 覆盖 |
| 无条件重启然后听天由命 | 强制冷启动,并验证 su 真的返回 uid=0 |
预编译二进制 —— 从 Releases 取对应平台的压缩包:
tar -xzf avdroot_linux_amd64.tar.gz
chmod +x avdroot_linux_amd64
sudo mv avdroot_linux_amd64 /usr/local/bin/avdroot
Windows 和 macOS 的包里是单个可执行文件;macOS 上可能需要清除隔离属性:
xattr -d com.apple.quarantine avdroot_darwin_arm64
每个 release 同时发布 SHA256SUMS.txt:
sha256sum -c SHA256SUMS.txt --ignore-missing
用 Go 安装(需要 1.24 或更新):
go install github.com/ejfkdev/avdroot@latest
版本下限是 1.24,因为更早的工具链不会生成 LC_UUID 加载命令,而 macOS 26 的
dyld 会直接拒绝启动这样的二进制。Go 1.21 和 1.22 还会把 minos 标记为
26.0,因此两条都不满足。
从源码构建:
git clone https://github.com/ejfkdev/avdroot
cd avdroot
make build # 本机二进制
make all # 交叉编译出所有发布目标到 dist/
你还需要带 adb 和模拟器的 Android SDK,以及 google_apis 或 AOSP 的系统镜像。
Play Store 镜像不能用这种方式 root(它不允许 adb root),所以 patch 和 root
会直接拒绝,而不是跑几分钟后才失败。
avdroot list # 看看有什么,处于什么状态
avdroot root # 打补丁、重启、装 App、授予 su、验证
就这些。只有一个 AVD 时 root 不需要指定名称,会询问一次确认,而当 stdin 不是终端时
自动跳过询问:
avdroot root Pixel_10_Pro_XL --yes # 指定 AVD,全自动
想分步执行也可以:
avdroot patch # 只打补丁;模拟器不必在运行
avdroot patch --dry-run # 只显示会改什么,不写入
# 自己重启模拟器,然后
avdroot install && avdroot verify
撤销:
avdroot restore # 从 ramdisk.img.backup 还原
| 命令 | 说明 |
|---|---|
avdroot root [目标] | 打补丁、重启、安装、授予 su、验证 —— 全自动 |
avdroot patch [目标] | 只给 ramdisk 打补丁;写入前会确认 |
avdroot restore [目标] | 从 ramdisk.img.backup 还原 |
avdroot list | 列出 AVD、系统镜像及其补丁状态 |
avdroot status [目标] | 查看某个目标的详情,含 preinit 分区 |
avdroot verify [目标] | 检查 Magisk 是否运行、su 是否返回 uid=0 |
avdroot install | 把 Magisk App 安装到运行中的模拟器 |
avdroot magisk info | 会使用哪个安装包,以及它支持什么 |
avdroot magisk fetch | 下载发行版(--list、--version、--prerelease) |
avdroot trust-chrome | 让 Chrome 接受中间人 CA(--cert、--clear) |
avdroot doctor | 说明每个路径是怎么找到的 |
「目标」可以是 AVD 名称、系统镜像路径,或 ramdisk.img 的路径。
avdroot <命令> --help 会给出该命令的示例。
对所有命令都有效:
| 参数 | 作用 |
|---|---|
--sdk PATH | 手动指定 Android SDK 根目录(自动识别不对时) |
--avd-home PATH | AVD 定义所在目录 |
--serial SERIAL | 连接了多个设备时指定用哪个 |
--magisk PATH | 使用指定的 Magisk 安装包 |
--abi ABI | 覆盖检测到的 ABI |
--compress FORMAT | 覆盖 ramdisk 压缩格式(gzip、lz4_legacy、lz4、xz) |
--preinit DEVICE | 覆盖 preinit 块设备 |
--lang | 覆盖系统语言(zh 或 en) |
--no-adb | 不连接运行中的模拟器 |
--no-download | 不自动下载 Magisk 安装包 |
| 参数 | 命令 | 作用 |
|---|---|---|
-y、--yes | root、patch | 跳过确认 |
--dry-run | patch | 只打印计划,不写入 |
--force | root、patch | 对已打过补丁的 ramdisk 也重新打 |
--no-install | root | 不装 Magisk App;能不能 root 与它无关 |
--no-restart | root、trust-chrome | 不动模拟器或浏览器 |
--timeout 10m | root | 等待启动的超时时间 |
--keep-verity、--keep-forceencrypt | patch | 默认都开启;关掉会去掉 fstab 里的对应选项 |
--recovery-mode | patch | 按 recovery 模式打补丁(API 28 自动启用) |
--cert PATH | trust-chrome | 指定要信任的 CA;省略则自动识别 |
--clear | trust-chrome | 撤销浏览器信任 |
--list | restore、magisk fetch | 只列出,不执行 |
现代系统镜像的 ramdisk.img 是 cpio newc 归档,用 gzip、lz4 或 xz 压缩。
Android 11+ 的镜像常常在一个压缩流里串联两个归档 —— 通用 ramdisk 和 vendor ramdisk。
打补丁就是:
internal/imgfmt)。LZ4 legacy 流按块解码,包括串联流产生的重复 magic。internal/cpio)。同名条目后者覆盖前者,与内核顺序解包
的语义一致。internal/magisk),复刻 boot_patch.sh:先检测并回滚已有
补丁;用 magiskinit 替换 /init(权限 0750);加入
overlay.d/sbin/{magisk,stub,init-ld}.xz;按需去掉 fstab 里的 verity 和加密选项;
把与原厂归档的差异记录到 .backup,其中 .backup/.rmlist 列出 restore 需要删除
的条目。序列化结果与 Magisk v31 的 magiskboot 字节兼容:inode 从 300000 开始编号,nlink
固定为 1,mtime 为 0,条目按名称字节序输出。internal/magisk/parity_test.go 会断言
本工具为真实 AVD 产出的条目集合、权限、.rmlist 内容和配置,与 Magisk 官方安装器
打出的 ramdisk 完全一致。
avdroot root 按设备真正需要的顺序把这些串起来,而这个顺序和通常的书写顺序并不一样:
/data/adb/magisk,也就是管理器 App 本会解压的那些文件。
这一步必须在重启之前完成:magiskd 只在启动时判断一次 su 是否可用,之后再补
文件只能等下一次启动才会被看到。su 真的返回 uid=0。Magisk 发布的是一个通用包,不是每个 Android 版本一个。一个发行版里就包含全部 ABI
的原生库,只要 Android 版本不低于它声明的 minSdkVersion 就能装: