SELinux(Security-Enhanced Linux)是 Linux 内核的一个强制访问控制(MAC)安全模块,由 NSA(美国国家安全局)开发,后被集成到 Android 系统中。
一、为什么需要 SELinux
传统 Linux 的安全问题
传统 Linux 使用 DAC(Discretionary Access Control,自主访问控制):
| 问题 | 说明 |
|---|---|
| root 权限过大 | root 用户可以做任何事,一旦应用获取 root,系统完全暴露 |
| 应用权限粒度粗 | 应用要么有权限,要么没有,无法精细控制 |
| 进程间缺乏隔离 | 进程可以任意访问其他进程的资源 |
例子:没有 SELinux 的危险
恶意应用获取 root 后:
-
读取其他应用的私有数据
-
修改系统 文件
-
注入代码 到其他进程
-
完全控制系统
二、SELinux 的核心机制
2.1 MAC vs DAC
| 特性 | DAC(传统 Linux) | MAC(SELinux) |
|---|---|---|
| 控制方式 | 资源所有者自主决定 | 系统强制策略决定 |
| root 限制 | root 不受限制 | root 也受策略约束 |
| 粒度 | 用户/组级别 | 进程/文件级别,精细到每个操作 |
| 灵活性 | 高(用户可改) | 低(策略由管理员定义) |
2.2 三个核心概念
┌─────────────────────────────────────────┐
│ 1. Subject(主体)= 进程 │
│ 如:Zygote 进程、某个应用进程 │
├─────────────────────────────────────────┤
│ 2. Object(客体)= 资源 │
│ 如:文件、目录、Socket、Binder 接口 │
├─────────────────────────────────────────┤
│ 3. Policy(策略)= 访问规则 │
│ 如:Zygote 只能读取 /system/bin/* │
│ 应用 A 不能访问应用 B 的数据 │
└─────────────────────────────────────────┘
2.3 安全上下文(Security Context)
每个进程和文件都有一个标签,格式为:
user:role:type:level
例如:
#Zygote 进程的安全上下文
u:r:zygote:s0
#系统文件的安全上下文
u:object_r:system_file:s0
#应用数据的安全上下文
u:object_r:app_data_file:s0
| 字段 | 含义 |
|---|---|
user |
SELinux 用户(通常固定为 u) |
role |
角色(进程为 r,文件为 object_r) |
| type | 核心:决定访问权限的类型 |
level |
安全级别(MLS/MCS,多用户隔离) |
三、SELinux 的工作模式
| 模式 | 说明 | 用途 |
|---|---|---|
| Enforcing | 强制模式,违反策略的操作会被拒绝 | 生产环境 |
| Permissive | 宽容模式,违反策略只记录日志,不阻止 | 调试、开发 |
| Disabled | 完全关闭 SELinux | 不推荐 |
bash
# 查看当前模式
adb shell getenforce
# 输出: Enforcing 或 Permissive
# 临时切换(需 root)
adb shell setenforce 0 # 切换到 Permissive
adb shell setenforce 1 # 切换到 Enforcing
四、Android 中的 SELinux 策略
4.1 策略文件位置

4.2 常见类型(Type)
| 类型 | 说明 |
|---|---|
zygote |
Zygote 进程 |
system_server |
SystemServer 进程 |
platform_app |
系统签名应用 |
untrusted_app |
普通第三方应用 |
shell |
ADB shell |
init |
init 进程 |
4.3 策略规则示例
bash
# zygote.te (Zygote 的策略文件)
type zygote, domain;
type zygote_exec, exec_type, file_type;
# Zygote 可以执行 zygote_exec 类型的文件
init_daemon_domain(zygote)
# Zygote 可以创建 /dev/socket/zygote
allow zygote zygote_socket:sock_file create_file_perms;
# Zygote 可以 fork 子进程
allow zygote self:process fork;
# Zygote 可以读取 /system 目录
allow zygote system_file:dir r_dir_perms;
# Zygote 不能访问应用私有数据(除非明确允许)
# neverallow zygote app_data_file:file *;
五、SELinux 在 Android 启动流程中的作用

init 进程中的 SELinux 初始化
c
// init 第一阶段
selinux_android_load_policy(); // 加载策略文件
selinux_restore_context("/dev"); // 恢复 /dev 目录的安全上下文
selinux_restore_context("/system"); // 恢复 /system 目录的安全上下文
六、实际案例:SELinux 如何保护系统
案例 1:阻止应用读取其他应用数据
bash
# 应用 A 的类型是 untrusted_app
# 应用 B 的数据文件类型是 app_data_file
# 默认规则:应用只能访问自己的数据
allow untrusted_app app_data_file:dir search;
# 但只能访问属于自己的 app_data_file 实例(通过 MLS 级别隔离)
案例 2:限制 Zygote 的权限
bash
# Zygote 虽然以 root 运行,但 SELinux 限制它:
allow zygote self:capability { setgid setuid }; // 只能使用这两个 capability
# 不能:kill 其他进程、修改系统文件、访问网络等
案例 3:Binder 调用控制
bash
# 只有特定类型的进程可以调用 system_server 的 Binder 接口
allow system_server zygote:binder { call transfer };
allow platform_app system_server:binder call;
# 普通应用 untrusted_app 没有这个权限
七、开发中常见的 SELinux 问题
| 问题 | 现象 | 解决 |
|---|---|---|
| avc: denied | 日志中出现 avc: denied { read } |
修改 .te 策略文件,添加 allow 规则 |
| 权限不足 | 应用无法访问某个文件 | 检查文件和进程的安全上下文是否匹配 |
| CTS 失败 | 兼容性测试不通过 | 确保策略符合 Android 安全规范 |
查看 SELinux 日志
bash
adb shell dmesg | grep avc
# 或
adb shell logcat -b events | grep selinux
# 示例输出:
# avc: denied { read } for pid=1234 comm="myapp" name="data" dev="dm-0"
# ino=12345 scontext=u:r:untrusted_app:s0:c512,c768
# tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0
八、总结
SELinux 是 Android 的"安全门卫" ------ 它通过强制访问控制策略,给每个进程和文件打上"身份标签",严格规定"谁能做什么",即使 root 用户也无法逾越策略限制。这让 Android 系统即使面对恶意应用或权限提升攻击,也能守住安全底线。