Android Seccomp深度解析:沙箱防护全流程

发布时间:2026/6/17 7:56:04

Android Seccomp深度解析:沙箱防护全流程 本篇文章剖析 Android 系统中SeccompSecure Computing Mode策略的完整实现方案。Android 自 8.0 (Oreo) 开始全面引入 Seccomp-BPF 机制核心目的是为了最小化内核攻击面。即便应用层App或系统服务遭到了远程代码执行RCE攻击由于 Seccomp 限制了其可以调用的系统调用Syscalls攻击者也无法利用未授权的内核漏洞完成提权。Android 的 Seccomp 实现横跨了编译期策略生成、Zygote 进程动态注入、原生守护进程的沙箱化以及内核层级过滤4 个核心阶段。一、 Android Seccomp 整体架构流向我们可以通过以下流程了解 Seccomp 是如何从普通的文本配置变成内核中的过滤网的。二、 核心实现步骤详解1. 编译期策略文件的定义与 BPF 字节码生成Android 并不会在运行时去解析可读的文本策略而是在系统编译阶段Build time将策略直接编译为 BPFBerkeley Packet Filter字节码以确保运行时的绝对高效。策略源文件位置位于bionic/libc/目录下。SYSCALLS.TXT定义了 Bionic C 库支持的所有系统调用。SECCOMP_BLACKLIST_APP.TXT应用进程的系统调用黑名单如阻止swapon、swapoff、chroot等高危调用。SECCOMP_WHITELIST.TXT系统允许的白名单。编译转换脚本AOSP 提供了 Python 脚本如genseccomp.py其核心逻辑为最终白名单SYSCALLS.TXT - SECCOMP_BLACKLIST_APP.TXT SECCOMP_WHITELIST.TXT生成产物脚本会为不同的 CPU 架构ARM, ARM64, x86, x86_64生成对应的 C 字节码数组文件例如arm64_app_policy.cpp内部包含密密麻麻的sock_filter结构体数组。例如C// 伪代码示例生成后的 BPF 过滤规则 static const struct sock_filter arm64_app_filter[] { BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), // 加载系统调用号 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_swapon, 0, 1), // 如果是 swapon BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP), // 触发异常拦截 ... BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // 默认允许 };2. 运行时Zygote 进程分支时的动态应用App 沙箱化Android 所有的应用程序进程都是由Zygote进程fork出来的。由于 Seccomp 策略一旦设定就只能增加不能减少单向不可逆因此 Zygote 自身不能过早启用严格的 App 策略必须在fork之后、执行应用代码之前进行精确注入。这一核心逻辑位于frameworks/base/core/jni/com_android_internal_os_Zygote.cpp中。核心代码追踪在 Zygote fork 应用进程的必经之路ForkAndSpecializeCommon函数中有如下调用C// 位于 com_android_internal_os_Zygote.cpp static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { // 1. 检查当前是否开启了安全强制模式如 userdebug 模式下执行了 setenforce 0 可能会关闭 if (!gIsSecurityEnforced) { ALOGI(seccomp disabled by setenforce 0); return; } // 2. 根据 UID 的不同应用不同的 Seccomp 策略 if (uid AID_APP_START) { if (is_child_zygote) { // 如果是 WebViewZygote 或 AppZygote 衍生出的子 Zygote set_app_zygote_seccomp_filter(); } else { // 普通的 Android 应用程序进程 set_app_seccomp_filter(); } } else { // 系统核心服务进程如 system_server set_system_seccomp_filter(); } }关键设计考量这一步调用的时机极其严格。它必须在fork()产生子进程之后执行且必须在子进程放弃CAP_SYS_ADMIN特权Dropping capabilities之前或设置PR_SET_NO_NEW_PRIVS之后执行。在 Android 中由于设置PR_SET_NO_NEW_PRIVS会破坏 SELinux 的域转换Domain Transition因此 Android 选择在 Zygote 还拥有特权时直接调用prctl加载策略紧接着再放弃特权。3. 底层调用Bionic 库与内核的交互上面提到的set_app_seccomp_filter()会向下调用到 Bionic 库中的bionic/libc/seccomp/seccomp_policy.cpp。在该文件中Android 通过调用 Linux 内核提供的prctl系统调用将编译好的 BPF 数组传递给内核C#include linux/filter.h #include sys/prctl.h // Bionic 内部加载 BPF 策略的实现 static bool install_filter(const struct sock_filter* filter, size_t filter_size) { struct sock_fprog prog { .len static_castunsigned short(filter_size), .filter const_castsock_filter*(filter), }; // 调用 Linux 内核机制注入 BPF 过滤器 // SECCOMP_MODE_FILTER 表示使用用户自定义的 BPF 规则过滤 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prog) 0) { PLOG(FATAL) Could not set seccomp filter; return false; } return true; }4. 原生守护进程Native Daemons的 Seccomp 实现除了应用进程Android 的媒体服务如mediacodec、mediaextractor也是黑客攻击的重灾区。针对这类原生 C/C 进程Android 采用了另外两种方式实现 Seccomp 隔离方案 A通过init.rc配置文件Android 10 推荐在服务的初始化配置文件.rc文件中直接通过seccomp关键字指定策略文件代码段service mediaextractor /system/bin/mediaextractor class main user mediaex group drmrpc mediadrm net_bt_admin # 指定该服务启动时加载的 seccomp 策略文件 seccomp /system/etc/seccomp_policy/mediaextractor.policyinit进程在解析到该行时会在fork出服务进程后主动读取该.policy文本文件并利用libseccomp动态编译成 BPF 注入该服务。方案 B使用Minijail开源工具在较老的版本或特定厂商实现中系统服务常通过 Google 的libminijail库来动态加载C// 守护进程内部代码示例 mini_jail* jail minijail_new(); minijail_parse_seccomp_filters(jail, /vendor/etc/seccomp_policy/mediacodec.policy); minijail_enter(jail); // 此后该进程便进入了 Seccomp 沙箱三、 攻击缓解效果当黑客通过利用如漏洞比如内存越界控制了某个 App 进程或mediacodec进程并企图执行 Shellcode 提权时触发拦截当 Shellcode 尝试调用诸如sys_reboot()、swapon()或高危的ptrace()在旧内核中可能导致逃逸时。内核处置Linux 内核在执行该系统调用前会先跑一遍该进程附带的 BPF 过滤代码匹配到禁止调用。发送信号内核会直接向该进程发送SIGSYS信号部分配置下为SIGKILL。进程崩溃进程瞬间被强行终止并在 Logcat 中留下类似Bad system call的崩溃日志。通过这种纵深防御的体系Android 成功将“单点内核漏洞直接沦陷整机”的风险降低为了“仅导致当前服务崩溃/闪退”极大地提升了内核的安全性。四、MediaTek (MTK) 的策略如果你是因为遇到了 SIGSYS 崩溃Seccomp 拒绝了某个系统调用或者需要为 MTK 的自定义 HAL 进程如音频、相机、编解码添加系统调用白名单MTK 平台有一套独立的、基于纯文本的配置方案根本不需要通过 Python 脚本去生成目标代码。MTK 自身的硬件服务和厂商定制进程的 Seccomp 策略使用的是纯文本的 .policy 文件。你可以去以下几个地方排查1.源码中的策略文件路径针对修改编译在你的 MTK 源码树中检查以下目录不同 OEM 可能会有微调vendor/mediatek/proprietary/hardware/seccomp/device/mediatek/mt8766/seccomp/或者类似的平台设备目录这些目录下会有类似 mediacodec-seccomp.policy 或者是针对特定厂商进程如 android.hardware.media.c2...-mediatek-seccomp-policy的文本文件。2.编译出厂后的生成路径针对快速验证这些文本策略最终会被打包复制到设备的 /vendor 分区。如果你手里有编译好的镜像或者处于 Root 状态的板子可以直接去这里查看/vendor/etc/seccomp_policy/在这个目录下你会看到一大堆以 .policy 结尾的文件里面直接用纯文本列出了允许该进程调用的系统调用列表例如 ioctl: 1、prctl: 1。如果发生了 SIGSYS 闪退直接在对应的 .policy 文件末尾追加缺失的系统调用然后重新编译 vendor 分区即可。五、 Seccomp 安全策略核心文件这一组.TXT文件是 Android 用来构建进程沙箱的基石。编译系统会读取它们并最终合并生成内核的 BPF 过滤规则。SYSCALLS.TXT作用系统调用总表。它记录了当前 Android 版本如 Android 14所支持的、各架构ARM64, x86等最原始的系统调用映射关系。Bionic 库会根据它来生成 C 语言函数如open()对应的汇编底层实现。SECCOMP_ALLOWLIST_APP.TXT作用普通应用程序App的系统调用白名单。所有从 Google Play 或第三方安装的普通 App被 Fork 出来后都会应用这个文件里的规则。只有在这个列表里的系统调用App 才有权调用。SECCOMP_ALLOWLIST_SYSTEM.TXT作用核心系统服务的系统调用白名单。像system_server、高级守护进程等它们需要的权限比普通 App 大得多例如需要特定的网络或进程控制权限所以它们的白名单范围会比 APP 的更宽。SECCOMP_ALLOWLIST_COMMON.TXT作用公共白名单。App 和系统服务都需要用到的最基础的系统调用如read,write,futex,mmap等为了避免重复编写全部抽离在这个公共文件里。SECCOMP_BLOCKLIST_APP.TXT/COMMON.TXT作用显式黑名单。明确禁止应用或所有进程调用的高危系统调用例如swapon/swapoff交换分区控制、reboot重启系统等。即使底层 C 库里有这些函数一旦调用内核也会直接用 Seccomp 杀掉进程。SECCOMP_PRIORITY.TXT作用性能优化优先级表核心安全细节。原理内核在执行 Seccomp 过滤时需要像执行代码一样逐行匹配 BPF 指令。如果把高频使用的系统调用放前面性能就会极高。这个文件指定了哪些系统调用如read,write,epoll_wait是“高频”的编译工具会根据它把这些调用排在 BPF判断树的顶端以防沙箱机制拖慢整机性能。

相关新闻