1x01 - Zygote 与进程创建
在 Android 中,几乎所有的应用进程都是由 Zygote 进程“孵化”(fork)出来的。Zygote 的存在是为了加快应用启动速度并优化内存占用。
1. Zygote 的启动与预加载
Zygote 是由 init 进程根据 init.rc 脚本启动的。它的核心任务是:
- 初始化 ART 运行时: Zygote 的底层实现为
app_process64/app_process32,在启动过程中完成 ART 虚拟机的初始化(而非启动独立的 VM 进程)。 - 预加载核心类与资源: 加载数千个常用的 Java 类(如
android.widget.*)和系统资源。 - 监听 Socket: 打开
/dev/socket/zygote,等待来自system_server的进程创建请求。
由于 Linux 的 Copy-on-Write (COW) 机制,所有从 Zygote fork 出来的子进程都会共享这些预加载的内存页,直到它们尝试修改这些内存。这极大地节省了系统内存。
2. 进程孵化流程:从 Socket 到 Fork
当用户点击图标启动应用时,ActivityManagerService (AMS) 会通过 Socket 向 Zygote 发送一个请求。
mermaid
sequenceDiagram
participant User
participant AMS
participant Zygote
participant App
User->>AMS: 启动应用
AMS->>Zygote: Socket 请求 (UID/GID/Args)
Zygote->>Zygote: fork() 创建子进程
Zygote->>App: specialize (降权)
App->>App: setuid/setgid → AID
App->>App: 丢弃 Capabilities
App->>App: 设置 SELinux 上下文
App->>App: 应用 Seccomp 策略
App->>AMS: 进程就绪2.1 ZygoteConnection 处理请求
在 Zygote 进程中,ZygoteServer 接收到连接后,会交给 ZygoteConnection 处理。
- 参数解析: 解析 AMS 传来的 UID、GID、Runtime Args、Capabilities 等参数。
- 权限校验: Zygote 会检查请求方的身份,确保只有
system_server等授权进程能请求创建新进程。
2.2 fork 与 specialize
调用 Zygote.forkAndSpecialize() 进入 Native 层:
fork(): 创建子进程。此时子进程拥有与 Zygote 相同的权限(root)。specializeAppProcess(): 这是安全的关键。子进程在执行任何应用代码之前,必须“降权”:- 设置凭证: 调用
setresuid()和setresgid()切换到应用的 AID。 - 丢弃 Capabilities: 移除不必要的 Linux 特权。
- 设置 SELinux 标签: 切换到
untrusted_app等受限上下文。 - 应用 Seccomp 策略: 限制系统调用。
- 设置凭证: 调用
3. Linux Capabilities 的丢弃
Linux Capabilities 将 root 权限细分为多个子项(如 CAP_NET_RAW, CAP_SYS_ADMIN)。
- Zygote 的权限: Zygote 运行在 root 权限下,拥有完整的 Capabilities。
- 降权过程: 在
specialize阶段,子进程会调用capset()。对于普通应用,所有的 Capabilities 都会被清空。 - 安全意义: 即使应用进程内发生了代码执行漏洞,攻击者也无法执行需要特权的操作(如挂载文件系统或修改网络配置),因为内核已经剥夺了这些能力。
4. 隔离增强:App Zygote 与 WebView Zygote
为了进一步收紧沙箱,现代 Android 引入了专门的 Zygote 变体:
- WebView Zygote: 专门用于孵化 WebView 渲染进程,拥有更严格的 SELinux 策略和更少的权限。
- App Zygote: 允许应用定义自己的 Zygote 进程,用于孵化该应用的隔离服务(Isolated Services),减少主进程被攻破后的影响。
5. 安全案例:Zygote 降权绕过风险
历史上曾出现过多个与 Zygote 降权流程相关的安全问题,典型场景包括:
5.2 SELinux 上下文切换时序问题
- 场景: 如果在
setcon()切换到untrusted_app上下文之前,应用代码已经开始执行,那么短暂的时间窗口内进程可能以zygote上下文运行。 - 风险: 攻击者可以通过竞态条件在此窗口内访问本不该访问的系统资源。
- 防御: 现代 Android 在
specializeAppProcess()中确保 SELinux 上下文切换先于任何应用代码执行。(注:关于 Android 8.0 具体将此切换提前到 fork 之后第一步的说法,未在主流文献中得到确认,实际实现可能因版本而异。)
参考(AOSP)
- 应用沙盒(UID 隔离、SELinux 沙盒与 seccomp 相关演进):https://source.android.com/docs/security/app-sandbox
- SELinux(enforcing/permissive、domain 概念):https://source.android.com/docs/security/features/selinux
- 架构概览(system_server/系统服务/运行时层级位置):https://source.android.com/docs/core/architecture
- Zygote(进程孵化、WebView Zygote/USAP 等现代行为):https://source.android.com/docs/core/runtime/zygote