reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
# Fuchsia / Zircon:handle 模型 / DFv2 驱动框架 / FIDL
<CORE RULE>
两条会把整条分析链带错的事实:
① **Zircon 的 handle 整数值只在当前进程内有意义**——它和 seL4 的 CPtr 是同一类坑(见 [[sel4-kernel]]);
② **驱动是用户态 component**——不在内核里,不是"另一种 kext/`.sys`"。
```
handle(进程本地数值)
→ 该进程的 handle 表
→ 内核对象
→ rights
```
这三者必须一起恢复。**只拿 handle 数值做跨进程对齐,一切结论都不可信。**
</CORE RULE>
## 一、handle 语义:数值不是对象的全局 ID
- handle 的整数**只对当前进程有意义**:**同一个数值在另一个进程里可能根本不对应任何 handle,也可能对应完全不同的内核对象**
- **handle 被关闭后,其数值允许被重用**
- 因此下面这条推理是**无效**的:
```
A: zx_channel_write(0x4f, ...)
B: zx_channel_read(0x4f, ...)
→ "同一个 channel"
```
正确做法是分别恢复两个进程各自的 handle 表,**按底层的 kernel object 对齐**,而不是按数值对齐。
## 二、handle 可以"随消息搬家":in-transit 与 transfer
- channel 不只是传字节,**还能传 capability**:handle 写进 channel 后,会**从发送进程移除**并挂到消息上,接收方读到后拿到**新的 handle**
- **传输途中属于 in-transit 状态**:只绑定在进程上或只绑定在内核中;**绑定在内核中的 handle 对用户态不可见**——"in-transit 的 handle 在用户态看不到"是设计如此,不是丢失
- 所以 trace 里出现下面这种序列**可能是完全正常的**,不是 UAF:
```
A 曾持有 handle 0x57
→ channel_write 把它送走
A 再访问 0x57 → BAD_HANDLE
```
- 权限约束:channel 自身需 `ZX_RIGHT_WRITE`;**被发送的每个 handle 需 `ZX_RIGHT_TRANSFER`**
- **写入失败不等于 handle 还在**:失败时 handle 被**丢弃而非转移**(写操作被改成"始终消费"语义)。所以"调用失败了,那它应该还持有这个 handle"是错的
- 原子性:一次写入中的 handle **要么全部进入 channel,要么全部被丢弃**
- 变体 `zx_channel_write_etc`:每个 handle 包在 `zx_handle_disposition_t` 里,操作为 **`ZX_HANDLE_OP_MOVE`**(等价于 `handle_replace` + write,源 handle 总是被关闭)或 **`ZX_HANDLE_OP_DUPLICATE`**(源 handle 保留)
- **rights 只能收窄不能放大**:可以在传输时降权,甚至可以置为 `ZX_RIGHT_NONE`;**若移除了 `ZX_RIGHT_TRANSFER`,接收方拿到的 handle 就无法再转手**——这是"为什么这一段传不下去了"的常见解释
- 特例:**in-transit 且它所在的 channel/socket 被销毁 → 该 handle 被关闭**
**推论**:reachability / security 关系图必须同时记录 **object + handle + rights**——两个 handle 可以指向同一个对象却持有不同的 READ / WRITE / MAP / TRANSFER 权限,"同一个对象"不等于"同样的能力"。
## 三、DFv2:驱动是用户态 component,绑定靠 bind rules 不是 probe
DFv2 的实体分工:
| 实体 | 职责 |
|---|---|
| **driver manager** | 启动/停止驱动、为驱动路由 FIDL capability、**维护全部节点(node)的拓扑** |
| **driver host** | 承载驱动实例的进程(独立地址空间与线程);**一个 driver host 可以同驻多个驱动** |
| **driver index** | 登记全部可用驱动及其元数据(component URL + **bind rules**),按请求对节点做匹配 |
| **driver runtime** | 同驻驱动之间的本地通信机制 |
| **FIDL** | 驱动之间、驱动与框架、驱动与非驱动组件之间的主要通信方式 |
绑定序列:
```
父驱动创建子节点
→ driver manager 向 driver index 发 MatchDriver
→ driver index 用各驱动的 bind rules(字节码)比对节点属性
→ 返回最匹配驱动的 URL
→ driver manager 创建/复用 driver host,启动驱动实例(Start())
→ 驱动可以再创建子节点,循环继续
```
**所以不要去找 Linux 式的 `bus probe()`**:应恢复的是
```
node properties → bind rules → driver index → driver component → driver host → FIDL services
```
驱动还有层级之分(boot driver / base driver / universe driver)——**"系统里没有这个驱动"先按加载来源分层看**,universe driver 是开发期手动登记的。
## 四、同驻驱动:syscall trace 里找不到 channel 调用,不一定是分析错了
- **同驻(co-located)**:驱动可以要求被放进父驱动所在的 driver host,从而**共享同一地址空间**
- 同驻驱动之间,**driver runtime 提供进程内通信路径**——比走内核 channel 更快,代价是**不产生 `zx_channel_write()` 这类 syscall 痕迹**
所以:**功能上明显发生了 driver A → driver B 的 IPC,但 syscall trace 里干干净净**——先怀疑 driver-runtime local transport,而不是"看漏了"或"样本在隐藏调用"。
- 相关的还有 node "symbols"(键值对,值可含虚拟地址):**仅在父子同驻同一个 driver host 时用于进程内通信**;不同驻时走 FIDL
- 反向推论:**看到大量直接内存地址交换,往往说明这两个驱动同驻**——这本身是一条拓扑证据,可用于恢复 driver host 划分
## 五、`/dev/foo` 不是 Unix 设备文件
- 打开 devfs 里的 entry 后,**拿到的通常是一个 FIDL channel**,之后的通信走 FIDL,而不是 read/write/ioctl 那套设备文件语义
- 新工具链还在**逐步从 devfs 转向 service discovery**:设备能力的暴露方式随年代变化,**"我在 `/dev` 里没找到它"不等于它不存在**
## 决策树
```
拿到 Fuchsia 目标
├─ 先问:这是用户态 component 还是内核?
│ ├─ 用户态(绝大多数驱动与系统服务)→ DFv2 模型
│ └─ 内核(Zircon 本体)→ 内核对象与 handle 表
├─ handle 相关
│ ├─ 跨进程比对 handle 数值 → 无效!改成按 kernel object 对齐
│ ├─ 曾经有 handle、现在 BAD_HANDLE → 先查是否被 channel_write 送走(in-transit/transfer)
│ └─ 传不过去 → 查 ZX_RIGHT_TRANSFER 是否被移除(rights 收窄过)
├─ 设备/驱动相关
│ ├─ 别找 bus probe() → 恢复 node properties → bind rules → driver index → driver host
│ ├─ 驱动"没加载" → 分辨 boot/base/universe 来源
│ ├─ 有 IPC 但无 syscall → 查是否同驻同一 driver host(driver runtime local transport)
│ └─ 找设备 → 别只翻 devfs,现代路径可能走 service discovery
└─ 权限/能力异常 → 画 object + handle + rights 三方关系图,而不是只看对象图
```
## 工具与验证
- 拓扑:driver manager 维护的 node 拓扑、driver index 的匹配结果(node → bind rules → driver URL)
- 组件与进程:driver host 是进程——**同驻与否可以从 driver host 划分直接读出来**,也是解释"无 syscall IPC"的关键证据
- 通信:FIDL 调用链(驱动与框架/其他组件的主路径)
- 验证:能同时说清「对象归属(哪个进程的哪个 handle)+ rights + 通信路径(FIDL / 同驻本地通道 / 内核 channel)」
## 该平台的坑(汇总)
- **把 handle 数值当全局对象 ID**:数值只在进程内有效,跨进程比对无意义,关闭后数值还会被重用
- **把 in-transit 当丢失**:绑定在内核中的 handle 对用户态不可见是正常状态
- **把"送走后再访问报 BAD_HANDLE"当 UAF**:transfer 成功后发送方本来就不再持有
- **以为写入失败 handle 就还在**:失败时是被丢弃而不是转移
- **忽略 rights**:同一对象上的两个 handle 能力可以不同;`ZX_RIGHT_TRANSFER` 被移除会让能力无法继续传递
- **按 Linux 思路找 bus probe()**:Fuchsia 的绑定是 node properties + bind rules 由 driver index 匹配
- **在 syscall trace 里找不到 IPC 就认为分析错了**:同驻驱动走 driver runtime 的进程内通道,本来就不产生内核调用
- **用 Unix 设备文件模型理解 `/dev/foo`**:打开后通常得到 FIDL channel;新代码还在转向 service discovery
- **把同驻驱动的内存地址交换当信息泄露或异常**:那是父子同驻 driver host 时的正常进程内通信机制