在 Fedora 44 上运行 Redroid(Android 13)—— 一份实战指南

太长不看版 —— Fedora + SELinux 给 Redroid 埋了三颗隐形地雷。本文从干净部署开始,逐一记录我踩到的具体故障模式,以及最终让 sys.boot_completed=1 的修复方法。已在 Fedora 44(GNOME Wayland)、内核 6.19.10、Docker 29.6.2、SELinux 强制模式、无 KVM 环境下验证通过。


目录

  1. 为什么选 Redroid?
  2. 第一部分 —— 如何部署
    • 前置条件:内核、binderfs、Docker
    • 拉取镜像
    • 运行容器
    • 用 adb 验证
    • 工具链:frida + Burp + CA 证书
  3. 第二部分 —— 问题、原因与修复
    • P1:容器不到 1 秒就退出(exit 129)
    • P2:vdc cryptfs init_user0 永久挂起
    • P3:所有 HAL 崩溃并报 "Could not register service"
    • P4:podman 瞬间杀死容器
    • P5:Android 应用无视全局代理(#p5
    • P6:重建容器导致一切丢失
    • 附赠:“最新”镜像不一定最好
  4. 恢复检查清单(容器重建后)
  5. 常见错误速查表

为什么选 Redroid?

想为移动应用渗透测试准备一个 Android 模拟器,但:

  • 机器是虚拟机:没有 /dev/kvm,没有 VT-x → Android Studio AVD 和 Genymotion 走不通(它们需要硬件虚拟化)。
  • 手边没有物理设备。

Redroid 将 Android 作为一个普通 Docker 容器运行。不需要 KVM,不需要 GPU —— 非常适合无特权的虚拟机环境。它启动的是基于 LineageOS 的 Android 13,暴露 5555 端口的 adb,默认有 root 权限,而且没有反模拟器检测的包袱。


第一部分 —— 如何部署

前置条件

1. 内核 —— binderfs(必须)

Android 的 IPC(Binder)需要 binder 设备。检查:

ls /dev/binderfs/            # 期望看到:binder、hwbinder、vndbinder(三个都要有)
grep binder /proc/filesystems   # 期望看到 "nodev binder"

如果缺失:

sudo modprobe binder_linux
# 持久化加载:
echo "binder_linux" | sudo tee /etc/modules-load.d/binder.conf
echo "binderfs /dev/binderfs binderfs nofail 0 0" | sudo tee -a /etc/fstab

不需要 ashmem —— Android 13 改用 memfd。

2. Docker(不要用 podman —— 见 P4)

sudo dnf install moby-engine        # Fedora 的 Docker(CE)包
sudo systemctl enable --now docker

3. 拉取 Redroid 镜像

sudo docker pull redroid/redroid:13.0.0-latest

在受限网络(如中国)中,可在 /etc/docker/daemon.json 中添加镜像加速器:

{"registry-mirrors": ["https://dockerproxy.net", "https://docker.m.daocloud.io"]}

运行容器

sudo docker run -itd --name redroid --privileged \
  -v /dev/binderfs:/dev/binderfs \
  -p 5555:5555 \
  redroid/redroid:13.0.0-latest \
  androidboot.redroid_gpu_mode=guest \
  ro.product.model="Pixel 6" ro.product.brand=google \
  ro.product.name=redroid ro.product.device=redroid \
  ro.product.manufacturer=Google \
  ro.debuggable=1 ro.secure=0

关键参数:

  • --privileged —— Redroid 需要挂载文件系统和操作 binder。必须。
  • -v /dev/binderfs:/dev/binderfs —— 将 binder 挂载点传入容器。
  • -p 5555:5555 —— adb 端口。
  • ro.debuggable=1 ro.secure=0 —— 使 adb root 生效。

用 adb 验证

adb connect 127.0.0.1:5555
adb devices                  # 期望看到 "device"
adb root                     # "adbd is already running as root"
adb shell getprop sys.boot_completed   # 期望 1
adb shell getprop init.svc.zygote      # 期望 running
adb shell getprop ro.build.version.release   # 13

健康系统下,Android 13 在 60~90 秒内完成启动。

安装渗透测试工具链

frida-server(版本须与主机 frida-tools 匹配):

# 主机:pip install frida-tools objection
# 下载 frida-server-<ver>-android-x86_64,然后:
adb push frida-server-17.16.4-android-x86_64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "sh -c '/data/local/tmp/frida-server > /dev/null 2>&1 &'"
frida-ps -U        # 验证进程列表是否显示

Burp 代理 + CA 证书(完整拦截链路):

adb reverse tcp:8080 tcp:8080                                     # 设备 → 主机 Burp
adb shell "settings put global http_proxy 127.0.0.1:8080"
# Redroid 特有问题:仅 settings 不够 —— 见 P5:
adb shell "setprop http.proxyHost 127.0.0.1; setprop http.proxyPort 8080;"
adb shell "stop && sleep 2 && start"                              # 重启框架
adb push 9a5ba575.0 /system/etc/security/cacerts/9a5ba575.0       # Burp CA(哈希命名)
adb shell "chmod 644 /system/etc/security/cacerts/9a5ba575.0"

验证:在模拟器内发起 HTTPS 请求(通过 frida 脚本在 system_server 中执行),然后观察 Burp 的 HTTP 历史中是否出现明文流量。已验证可行:UA=Dalvik/2.1.0 (Linux; U; Android 13; redroid13_x86_64),访问 https://www.baidu.com → HTTP 200 解密成功。

jadx 用于静态分析:sudo dnf install jadx(版本 1.5.0)。


第二部分 —— 问题、原因与修复

P1:容器不到 1 秒就退出(exit 129,日志为空)

现象

docker run 成功,然后容器约 0.15 秒内退出,代码 129(128+SIGHUP)。docker logs 完全为空。在 SELinux 强制模式的 Fedora 上,首次 dmesg -T | grep init 显示:

init: Failed to initialize property area
init: ... InitFatalReboot ...

可能原因 —— 有两层:

  1. Fedora 的 Docker 服务在 systemd 单元中硬编码了 --selinux-enabled。每个容器挂载都被打上了固定的 SELinux 上下文(system_u:object_r:container_file_t:s0:c1022,c1023),挂载点不再携带 seclabel 标签。Android 的 init 在 /dev/__properties__/* 上调用 fsetxattr(security.selinux) 时失败,返回 EOPNOTSUPP(errno 95)。
  2. 即使解决了第一层,精简的主机 SELinux 策略缺少 Android 所需的约 263 个类型(如 properties_serial),导致上下文写入时返回 EINVAL。

修复(同时解决两层):

禁用 Docker 的 SELinux 集成:

# /etc/docker/daemon.json
{"selinux-enabled": false, "registry-mirrors": ["..."]}

# Fedora 在单元文件中硬编码了该标志 —— 通过 drop-in 文件覆盖:
sudo mkdir -p /etc/systemd/system/docker.service.d
# /etc/systemd/system/docker.service.d/selinux-off.conf
# [Service]
# ExecStart=
# ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock \
#   --userland-proxy-path /usr/bin/docker-proxy --init-path /usr/bin/tini-static

sudo systemctl daemon-reload && sudo systemctl restart docker
docker info | grep -i selinux     # 确认已消失

修补镜像中的 Android 属性上下文,将其改为主机策略实际允许的类型:

# 从主机策略中挑选真实存在的 file_type 类型(如 abrt_etc_t、bin_t)
sed -i 's/u:object_r:[a-z_0-9]*:s0/system_u:object_r:abrt_etc_t:s0/g' plat_property_contexts
echo "(default system_u:object_r:abrt_etc_t:s0)" >> plat_property_contexts

# 此外还需二进制字符串修补:
#  /system/lib64/bootstrap/libc.so : "u:object_r:properties_serial:s0"
#  /system/bin/init               : "u:object_r:default_prop:s0"

通过 docker create + docker cp + docker commit 重新打包(不要用 docker import —— tar 包内含设备节点,containerd 无法解包):

CID=$(docker create --name redroid-fix redroid/redroid:13.0.0-latest)
docker cp plat_property_contexts $CID:/system/etc/selinux/plat_property_contexts
docker cp init $CID:/system/bin/init
docker cp libc.so $CID:/system/lib64/bootstrap/libc.so
docker commit $CID redroid-fixed:13

P2:vdc --wait cryptfs init_user0 挂起 —— 启动无法继续

现象: 容器存活,但 init 显示 Exec service is hung? Waited Ns without SIGCHLD。一个 PPID=1 的子进程(vdc --wait cryptfs init_user0)一直处于 nanosleep 状态。Zygote 和 adbd 永不启动。

可能原因: 镜像中没有 fstab → vold 从未初始化 → Redroid 的自定义 init.rc 在 post-fs-data 阶段无条件执行 init_user0(该内置命令会 fork 出 vdc 并等待 vold)→ 永久等待。检查:getprop ro.crypto.state 为空,/system/etc/fstab* 缺失。

修复:

# 1) 注释掉 init.rc 中的 init_user0 行
# grep -n init_user0 /system/etc/init/hw/init.rc   → 用 sed 注释该行
sed -i 's/^\(.*init_user0.*\)$/#[FBE-skip] \1/' /system/etc/init/hw/init.rc

# 2) 提供一个最小的 fstab(注意:共 5 个字段!fs_mgr_flags 是第 5 个字段)
cat > fstab.redroid <<'EOF'
# Android fstab file.
#<src> <mnt_point> <type> <mnt_flags> <fs_mgr_flags>
/dev/block/vdc /data ext4 rw,nosuid,nodev,noatime,noauto_da_alloc,reservedsize=128M,quota formattable,check
EOF
# 复制到 /system/etc/fstab.redroid 和 /system/etc/fstab 两个位置

# 3) 告诉 zygote 分支该设备未加密:
printf 'ro.crypto.state=unencrypted\nro.crypto.type=none\n' > /system/etc/prop.default

# 重新提交镜像

提示:同时将 init.rc 中剩余的 5 行 exec ... vdc checkpoint/keymaster 替换为 /system/bin/true,否则它们也会阻塞。


P3:所有供应商 HAL 崩溃 —— "Could not register service (-2147483648)"

现象: 所有供应商 HAL 进程(keymaster、gatekeeper、wifi、hidl_memory)反复崩溃;surfaceflinger 报告 gralloc-mapper is missing/var/log/audit/audit.log 显示:

avc: denied { set_context_mgr } scontext=system_u:system_r:spc_t:s0
     tcontext=system_u:system_r:spc_t:s0 tclass=binder

可能原因: 精简的主机策略缺少 allow spc_t spc_t: binder set_context_mgr —— 因此 servicemanager / hwservicemanager 永远无法成为 binder 上下文管理器,所有服务注册失败。

修复 —— 为 spc_t 创建一个很小的许可型模块(比逐条添加 allow 规则更干净,一次性放通容器内部所有 avc 检查,主机策略保持不变):

# /tmp/redroid_perm.te
# module redroid_perm 1.0;
# require { type spc_t; }
# permissive spc_t;

sudo checkmodule -M -m -o /tmp/redroid_perm.mod /tmp/redroid_perm.te
sudo semodule_package -o /tmp/redroid_perm.pp -m /tmp/redroid_perm.mod
sudo semodule -i /tmp/redroid_perm.pp

注意:checkmodule 要求模块名与输出基名一致(redroid_permredroid_perm.mod)。


P4:podman 瞬间杀死容器(129)—— Docker 则正常

现象: 使用 podman(rootful)时,Redroid 容器在 0.15 秒内以 exit 129 退出,日志为空;而用 Docker 运行同一镜像则正常。

可能原因: podman 与 Android 的 pid-1 init 进程不兼容(信号处理 / conmon 交互问题 —— 向 Android init 发送了 SIGPHUP)。不值得深究。

修复: 使用真正的 Docker(moby-engine)。已确认:podman 下前台运行约 25 秒后退出,而 Docker 下长期稳定运行。


P5:settings put global http_proxy 被应用忽略

现象: 代理已设置,但 Java/OkHttp/WebView 流量从未到达 Burp;纯 HTTPS 请求超时。

可能原因: 在原生 Android 中,ConnectivityService 会将全局代理同步到 系统属性 http.proxyHost / http.proxyPort,Java 运行时在进程启动时会快照这些属性。而 Redroid 模拟的 ConnectivityService 从未执行该同步 —— getprop http.proxyHost 始终为空。

修复:

adb shell "setprop http.proxyHost 127.0.0.1; setprop http.proxyPort 8080;"
adb shell "stop && sleep 2 && start"    # 重启框架,让新进程读取这些属性

同时配合 adb reverse tcp:8080 tcp:8080,使模拟器无需知道主机 IP 即可访问 Burp。

验证完整链路:在 system_server 内运行一个 frida 脚本,通过 HttpURLConnection 打开 https://... —— 它会遵循系统代理;观察 Burp 显示明文。


P6:重建容器导致 frida、CA 证书和代理全部丢失

现象: docker rm + 再次 run 之后,突然发现 frida-server 提示 No such file or directory,CA 证书丢失,代理设置消失。

可能原因: 你在运行中的容器内推送的所有东西都存在于其可写层中。删除容器即丢弃该层 —— 这里 Docker 没有持久化的 /data 卷。

修复: 将其视为 6 步恢复检查清单(见下文),或添加持久化卷:

-v ~/.redroid_data:/data

P7:“最新镜像不一定是最好的镜像”

这一点耗费了我不少时间:镜像迭代链最终停在 redroid-fixed9:13,但 fixed9 永远达不到 sys.boot_completed(zygote 持续重启),而 redroid-fixed2:13 却能干净启动,且整个 frida/Burp 链路全部通过。永远不要假设最新 tag 最健康 —— 在每个候选镜像上验证启动状态,固定使用能工作的那个(在命令中明确指定 tag,如 redroid-fixed2:13)。


恢复检查清单(容器重建后)

docker rm + docker run 之后,按顺序执行:

adb connect 127.0.0.1:5555 && sleep 45          # 等待启动

adb push frida-server-17.16.4-android-x86_64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server && sh -c '/data/local/tmp/frida-server > /dev/null 2>&1 &'"

adb push 9a5ba575.0 /system/etc/security/cacerts/9a5ba575.0
adb shell "chmod 644 /system/etc/security/cacerts/9a5ba575.0"

adb reverse tcp:8080 tcp:8080
adb shell "settings put global http_proxy 127.0.0.1:8080"
adb shell "setprop http.proxyHost 127.0.0.1; setprop http.proxyPort 8080; setprop http.nonProxyHosts 'localhost|127.0.0.1|10.*|192.168.*'"

adb shell "stop && sleep 2 && start"           # 使代理生效

常见错误速查表

现象 可能原因 修复方法
容器以 129 退出,日志为空 SELinux 属性区域 init 失败 见 §P1(docker drop-in + 镜像上下文修补)
启动卡在 vdc cryptfs init_user0 镜像内没有 fstab 见 §P2:注释 init_user0,添加 fstab + prop.default
HAL 服务 SIGABRT + register -2147483648 SELinux 拒绝 set_context_mgr 见 §P3:为 spc_t 添加许可型模块
podman 下立即退出 podman / init 不兼容 使用 Docker(moby-engine)
应用无视全局代理 http.proxyHost 从未设置 见 §P5:setprop + 重启框架
重建后 frida/CA/代理丢失 状态仅存于可写层 恢复检查清单 + -v 挂载 /data 卷
镜像 N+x 上 sys.boot_completed 为空 后续镜像存在回归 固定使用已知良好的 tag

已在以下环境测试:Fedora 44 Workstation(GNOME Wayland),内核 6.19.10,Docker 29.6.2(moby-engine),SELinux 强制模式并加载精简自定义策略,无 KVM。使用的镜像:redroid-fixed2:13(Android 13,基于 LineageOS),frida 17.16.4,Burp CA,端到端验证通过(含 HTTPS 解密)。

实用笔记:大文件下载可借助镜像代理(gh-proxy/dockerproxy 在受限网络可用);docker import 会损坏 redroid 镜像(请使用 create+cp+commit);iptables -L 不会显示控制器 NAT 规则(实际是 nftables);Redroid 内无 curl/浏览器 —— 可用 frida 脚本在 system_server 中测试连通性。

posted on 2026-08-06 14:16  HOTSFMOC  阅读(2)  评论(0)    收藏  举报