7、Binder安全模型基础:SELinux与Binder的结合、安全上下文检查、binder_selinux.h分析

好,咱们今天聊点硬核的。Binder 的安全模型,说白了就是 Android 系统里的一道「安检门」。你想想看,一个 App 想调用系统服务,比如读取联系人、发送短信,总得有个机制来管一管吧?这个机制,就是 SELinux 和 Binder 的深度结合。

我个人习惯把 Binder 通信比作「跨进程快递」。快递员(Binder 驱动)负责把包裹(数据)从 A 送到 B。但问题是,A 有没有权限寄这个包裹?B 有没有权限收?包裹里有没有违禁品?这些,就是 SELinux 要管的事。

7.1 SELinux 与 Binder 的结合:从「自由放任」到「强制管控」

在 SELinux 引入之前,Android 的权限模型主要靠 UID/GID 来区分。说白了,就是看进程的「身份证号」。但这种方式有个致命缺陷——一旦进程被提权,或者被注入恶意代码,它就能为所欲为。

我记得在 Android 4.x 时代,我帮客户分析过一个提权漏洞。攻击者利用一个系统服务的 UID 漏洞,直接拿到了 system 权限。那时候的防护,真的跟纸糊的一样。

SELinux 的加入,彻底改变了这个局面。它引入了「强制访问控制」(MAC)。什么意思呢?就是系统定义了一套规则,所有进程都必须遵守。哪怕你是 root 用户,也不能违反规则。

在 Binder 通信中,SELinux 主要做了三件事:

  • 标记对象:每个 Binder 实体(服务端)和 Binder 代理(客户端)都被打上安全上下文标签。
  • 检查权限:在 Binder 事务(transaction)发生时,检查发送方和接收方的安全上下文是否匹配。
  • 审计日志:如果检查不通过,记录详细的 avc(Access Vector Cache)拒绝日志。

说白了,SELinux 就是给 Binder 通信装了一个「门禁系统」。每个进程进出,都要刷卡验证。

7.2 安全上下文检查:Binder 事务的「安检流程」

当一个进程通过 Binder 调用另一个进程的服务时,内核里的 Binder 驱动会触发一次安全上下文检查。这个检查发生在 binder_transaction 函数中。

我给大家画个流程图,方便理解:

Binder 事务安全上下文检查流程 客户端进程 安全上下文: u:r:app:s0 Binder 调用 Binder 驱动 binder_transaction() 安全上下文检查 传递事务 服务端进程 安全上下文: u:r:system_server:s0 安全策略决策 selinux_android_binder_*() ✅ 允许访问 事务正常传递 ❌ 拒绝访问 返回 -EPERM 📝 审计日志 (avc)

这个检查的核心,就是比较两个安全上下文。安全上下文长什么样呢?举个例子:

u:r:untrusted_app:s0:c15,c256,c513,c768

这个字符串包含四个部分:

  • u:用户(user),Android 里统一是 u。
  • r:角色(role),一般也是固定的。
  • untrusted_app:类型(type),这是最关键的部分,决定了进程的权限。
  • s0:c15,c256,...:安全级别(MLS),用于多级安全策略。

检查时,Binder 驱动会调用 selinux_android_binder_* 系列函数。这些函数会查询 SELinux 策略数据库,判断「发送方类型」是否有权限向「接收方类型」发送 Binder 调用。

核心逻辑:检查的是「发送方」对「接收方」的 binder_call 权限。不是检查接收方能不能收,而是检查发送方有没有资格发。

7.3 binder_selinux.h 分析:安全模型的「源代码」

好,咱们来看看具体的代码。文件 binder_selinux.h 位于内核源码的 drivers/android/ 目录下。这个文件虽然不大,但它是 Binder 安全模型的「心脏」。

我打开这个文件,第一眼看到的就是几个关键函数声明:

// 检查 Binder 节点(服务端)的安全上下文
int selinux_binder_init_secctx(struct binder_node *node);

// 检查 Binder 事务的安全上下文
int selinux_binder_transaction(struct binder_proc *sender,
                               struct binder_proc *target,
                               struct binder_transaction *t);

// 检查 Binder 传输(transfer)的安全上下文
int selinux_binder_transfer(struct binder_proc *from,
                            struct binder_proc *to,
                            struct binder_node *node);

这三个函数,覆盖了 Binder 通信的三个关键环节:

函数名 检查时机 检查内容
selinux_binder_init_secctx 服务端注册 Binder 服务时 服务端进程是否有权注册该服务
selinux_binder_transaction 客户端发起 Binder 调用时 客户端是否有权调用该服务
selinux_binder_transfer Binder 对象跨进程传递时 源进程是否有权将对象传给目标进程

我曾经在分析一个系统服务漏洞时,就卡在了 selinux_binder_transfer 这个函数上。当时有个恶意 App 通过 Binder 把一个文件描述符传给了另一个高权限进程。按理说这是不允许的,但为什么没被拦截?

后来我发现,问题出在安全上下文的配置上。那个文件描述符的标签被错误地设置成了 u:r:system_file:s0,导致 SELinux 策略认为它是系统文件,允许了传输。嗯,这就是典型的「标签错误导致安全漏洞」。

7.4 安全上下文的传递与继承

在 Binder 通信中,安全上下文不是一成不变的。它会在进程间传递,甚至被继承。举个例子:

当一个 App 通过 Binder 调用 ActivityManagerService 时,AMS 会代表 App 执行一些操作。这时候,AMS 的安全上下文是 system_server,而不是 App 的 untrusted_app

这就引出一个问题:AMS 会不会「滥用」自己的权限?

答案是:不会。因为 SELinux 策略中定义了「域转换」(domain transition)规则。AMS 在执行 App 请求的操作时,会临时切换到 App 的安全上下文,或者使用一个中间上下文。这就是所谓的「SELinux 上下文切换」。

避坑指南:我曾经在开发一个自定义系统服务时,忘记配置域转换规则。结果服务端进程一直用自己的高权限上下文执行操作,导致 SELinux 审计日志里全是拒绝记录。后来我加了一条 type_transition 规则,才解决问题。

7.5 实战:如何查看和调试安全上下文

在实际开发中,我们经常需要查看进程的安全上下文。这里有几个常用命令:

# 查看某个进程的安全上下文
ps -Z | grep com.example.app

# 查看 Binder 节点的安全上下文
ls -Z /dev/binder

# 查看 SELinux 策略中关于 Binder 的规则
sesearch -A -s untrusted_app -t system_server -c binder -p call

如果遇到 Binder 调用被拒绝的情况,第一件事就是看 dmesglogcat 中的 avc 日志:

avc: denied { call } for pid=1234 comm="app_process"
scontext=u:r:untrusted_app:s0
tcontext=u:r:system_server:s0
tclass=binder

这条日志告诉我们:untrusted_app 想调用 system_server 的 Binder 服务,但被拒绝了。原因就是缺少 call 权限。

注意:avc 日志中的 scontext 是发送方,tcontext 是接收方。很多人会搞反,以为 scontext 是服务端。其实 s 代表 source(源),t 代表 target(目标)。

7.6 小结:Binder 安全模型的精髓

说了这么多,其实核心就一句话:SELinux 给 Binder 通信加了一把「锁」,这把锁的钥匙就是安全上下文

我个人觉得,理解 Binder 安全模型的关键,不在于记住那些函数名,而在于理解「谁可以访问谁」这个逻辑。每次看到 avc 拒绝日志,我都会问自己三个问题:

  1. 发送方是谁?它的安全上下文是什么?
  2. 接收方是谁?它的安全上下文是什么?
  3. 策略中是否允许这种访问?

把这三个问题搞清楚了,Binder 安全模型也就掌握得差不多了。

嗯,最后提醒一句:别小看 binder_selinux.h 这个文件。虽然它只有几百行代码,但它是整个 Android 系统服务安全的基石。我建议你把它打印出来,贴在工位上,没事就翻翻。


公众号:蓝海资料掘金营,微信deep3321