当高频观测不再经过异常路径:ShadowCave与常驻式插桩架构"

admin 2026-10-18 01:02:28 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

当高频观测不再经过异常路径:Shadow Cave 与常驻式插桩架构

mb\_iiulbilc
mb\_iiulbilc

看雪学苑

2026年8月28日 17:59
上海

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

传统动态插桩通常将一次命中建模为异常、回调或翻译事件:线程被捕获、现场被保存、观测逻辑被分派,原指令执行后再恢复。

该模型语义清晰,但在高频逐次观测场景下,异常往返和运行时分派会迅速成为主要开销,同时直接修改目标代码页又会破坏进程对自身代码的原始视图。

Shadow Cave 建立在 wxshadow 的 original/shadow 双视图之上,将观测代码生成并发布到 shadow 执行视图中的已证明区域,通过页内分支进入 Cave,完成 ARM64 指令重定位、寄存器与状态观测,再返回原始控制流。稳态命中不再依赖异常、单步或跨核同步。signal、同步 fault、fork、lease 破坏和并发退役则由精确 PC 相位、rendezvous、logical retire 与代际 tombstone 协议负责收敛。

本文从控制流证明、指令语义重建、观测事务、生命周期管理和时序侧信道五个方面介绍 Shadow Cave,并给出真机重定位对拍、逐次寄存器观测、并发退役、fork 隔离、signal/fault 矩阵及真实 Android 应用负载验证结果。Shadow Cave 的核心变化并非把传统 Hook 做得更快,而是将观测从拦截之后的处理改写为执行本身的一部分。

先从一个很普通的问题说起。

我们想知道某个函数每次被调用时,寄存器里装了什么。比如在某大型 Android 应用中,想确认 libart 的 monitor_enter 每次拿到的是哪个对象锁.如果每次进入都停下来检查,交互响应很快就会出现肉眼可见的卡顿。

最直接的办法是在门口放一个断点:线程走到这里,先停下来,进入内核,保存现场,交给 handler 检查,再把原指令补执行后放行。

偶尔检查一次这套办法很好,可如果这个门口每秒经过一百万人,检查本身就会变成主要工作。

如果直接改造门口,把目标指令换成跳板,绕到一段观测代码再回来。异常没有了,速度快了.但任何读取自身代码的完整性检查都会发现,门已经不是原来的门。

于是问题变成了:

能不能既不让每个线程停下来,也不让读取者看到代码被改过,
还能够对百万级命中逐次记录,而不是靠采样碰运气?

这篇文章是前两篇工作的继续。如果是第一次接触这套系统,建议按下面的顺序阅读:

  • 《ARM64 页内 XOL、陷阱垫与无损续执行架构》:解决断点命中以后,原指令怎样在 ARM64 上无损续执行。
  • 《从无痕 Hook迈向进程级双视图虚拟化》:解决同一进程中 original/shadow 两个物理视图如何保持逻辑自洽。

第一篇建立了 BRK/XOL 的异常兜底通道,第二篇让同一个虚拟地址可以由 CPU 取指看到 shadow、由普通数据读取看到 original。

Shadow Cave 则继续向前走了一步,既然执行者已经活在 shadow世界里,就在这个世界内部修一条只有取指能够进入的旁路,让观测本身成为普通指令流。

在决定做 Cave 之前,我比较过几条现实路线:

如果只需要分支轨迹并且设备开放完整 ETM,硬件 trace 往往更自然,如果只有几个诊断点BRP 也更简单。

Shadow Cave 面向的是更苛刻的交集:普通 Android 设备上的动态部署、外部 original 视图、百万级逐次命中、可编程寄存器观测,以及长期运行后的 signal、fault、fork、mprotect 和并发退役正确性。

即使刻意让异常方案占便宜,只把 401ns 当作一次命中的下界,而把 Cave 的 4.5ns 按全量寄存器观测计算。

一百万次命中也分别消耗约 401ms 与 4.5ms 单核时间,接近两个数量级。前者还没有加入完整 payload 的聚合和输出。走到这里,问题已经不是再优化一点 handler,而是必须把高频命中移出异常路径。

因此我没有废弃 BRK/XOL,而是把通道分层:低频、一次性、需要修改寄存器或无法安全重定位的锚点继续走异常通道,满足死区证明与重定位约束的高频观测锚点进入 Cave,前者负责语义兜底,后者承担吞吐。

Shadow Cave 建立在 wxshadow 已有的 original/shadow 双视图、执行页翻转和生命周期框架之上;本文新增并重点讨论的是页内死区证明、ARM64 指令重定位、lease 观测与事务化退役。

它不是对基础设施来源的重新命名,而是在其上生长出的常驻执行层。

一、BRK 与 Cave 到底差在哪里

线程抵达锚点以后,要经过哪些边界才能继续执行:

BRK / XOL 异常路径

 EL0 用户态                 EL1 内核态                         EL0 用户态
┌──────────┐   同步异常   ┌────────────────────────────┐   异常返回 ┌──────────┐
│ anchor   │ ───────────→ │ 保存现场 → handler         │ ─────────→ │ anchor+4 │
│   BRK    │              │ → 模拟/XOL → 修正 pt_regs  │            │ 继续执行 │
└──────────┘              └────────────────────────────┘            └──────────┘
      每次命中都跨越 EL0/EL1;异常帧替我们冻结执行点、保存上下文

Shadow Cave 稳态路径

 EL0 用户态:shadow 执行视图
┌──────────┐    ┌──────────────────────────────────────┐    ┌──────────┐
│ anchor   │ B  │ cave:可选观测 → relocated original  │ B  │ anchor+4 │
│ B cave   │ ─→ │       → 恢复临时状态                 │ ─→ │ 继续执行 │
└──────────┘    └──────────────────────────────────────┘    └──────────┘
      稳态不进入内核、不单步、不经过运行时回调;命中本身就是普通指令流

这并不是说 Cave 从此没有异常。线程仍可能在 Cave 内收到 signal,也可能发生同步 fault页面和 lease 也仍会被 mprotect、munmap 或 fork 影响。

区别在于这些异常不再是每次观测必经的入口,而是低概率边界条件。快路径与异常处理从此被拆开了。

再叠加双视图Cave 的位置才完整:

同一个虚拟地址 VA

CPU 取指 ─────────→ shadow PFN:anchor 已变成 B cave,页内含 Cave 代码
普通数据读取 ─────→ original PFN:仍是未经修改的原始字节
完整性检查/GUP ───→ 在双视图契约覆盖范围内仍观察 original

因此普通 code cave 与 Shadow Cave 的相似只停留在跳过去再跳回来。

前者通常把补丁和trampoline 放进所有读取者都能看到的地址空间。

后者把旁路放在 shadow 执行世界里,让执行者经过它,同时让数据读取者继续面对 original。

所以这里所说的代差不是一句比 BRK 快九十倍就能概括。性能只是结果。

真正的变化是观测不再以异常事件的身份出现,而成为另一个物理执行视图中的常驻代码。

相比传统方案,核心优势可以压缩成六点

其中最重要的不是某一个单点优势,而是这些能力能够同时成立:既保留 original 读取视图,又让高频观测留在普通执行流;既追求接近原生的稳态成本,也为 signal、fault、fork 和退役。

保留明确的收敛路径。Shadow Cave 的价值正是在这个交集,而不是在所有目标上替代 ETM、BRP、BRK/XOL 或 DBT。

二、一条 Cave 从出生到退役

普通 code cave 只回答哪里有块空地。Shadow Cave 必须回答的是:这块空间凭什么安全,原指令如何搬迁,观测如何发布,线程中途被打断怎么办,缓冲被销毁怎么办,以及什么时候才能真正回收。把这些问题按生命周期排开,整个系统会清楚很多:

             安装期                         运行期                         退役期

候选页 → 死区证明 → 指令重定位 → 生成 Cave → 发布锚点
                                              │
                                              ▼
                                      普通命中 / 观测写入
                            (快路径无异常;signal/fault 退化为慢路径兜底)
                                              │
                           ┌──────────────────┼──────────────────┐
                           ▼                  ▼                  ▼
                     signal/fault       lease/VMA 变化       主动删除锚点
                           │                  │                  │
                           └──────────────→ logical retire ←────┘
                                              │
                                      full-mm rendezvous
                                              │
                                   无在飞执行流的权威证明
                                              │
                                   dormant / physical reclaim

这张图里最重要的一点是:删除管理记录不等于 CPU 已经离开 Cave。从安装到回收,每一步都必须有自己的提交条件。

2.1 入场资格:先证明空间,再证明指令能搬

Cave 使用的是 shadow 页中被证明不会被正常控制流取指的字节。它依赖双视图不变量:

CPU 取指 → shadow
数据读取 → original

因此同一段 VA 在 original 中保留未经修改的原始字节,在 shadow 中则可以承载观测指令。

读取者看到的仍是原内容,只有执行者能够进入 Cave。

Cave 区域不能依靠字节模式猜测。literal pool、跳转表、异常恢复入口和间接分支目标都可能被错误识别,因此选址必须来自控制流与元数据证明:

PROVEN        编译/链接期显式 cave map,或可信离线工具结合
              ELF、符号、重定位和控制流信息给出证明
CONSERVATIVE  具备静态证据并经过人工核验的区域,逐目标签字
HEURISTIC     静态不可达推断或动态画像;生产环境默认拒绝

内核不负责猜测控制流,只验证提交结果:页内、对齐、不与锚点及已有 Cave 重叠,并且锚点指令属于可重定位集合。直接分支目标可以通过静态工具排除;jump table、BR/BLR间接目标仍需要编译期地图或人工证明。

空间合格以后,还要证明原指令能够搬过去。真正需要重定位的不是四字节机器码,而是它在原地址上本来会产生什么语义:

其中最容易错的是 LR:若直接在 Cave 里执行 BL,硬件会把 Cave 后继地址写进 x30,被调函数返回后就不再回到原控制流。因此 anchor+4 不只是一个返回地址,而是必须重建的原地址语义。

2.2 安装阶段:先建好旁路,再开放入口

通过选址与重定位后,系统才能生成 Cave。最短的直通形态只有两段动作:

anchor:  B cave

cave:
    <relocated original instruction>
    B anchor+4

观测形态则在原指令之前插入状态采集。安装过程遵循“先准备目的地、后发布入口”的顺序。

先在 shadow 死区生成完整 Cave,完成必要的 D-cache clean、I-cache invalidate 与取指同步,确认代码生成与重定位全部成功后,才把 anchor 发布为 B cave。这样任何已经看到新 anchor 的 CPU,都只能到达一份已经完成发布的 Cave,而不会撞进半写状态。

锚点发布也是 Cave 生命周期的第一个提交点。在它之前,失败可以直接放弃,目标控制流没有变化;在它之后,Cave 就成为真实可执行路径,后续修改必须进入 retire 协议,不能再把它当成一段普通缓冲区覆盖。

2.3 稳态命中:观测成为普通指令流

线程命中 anchor 后,不制造异常,直接进入 Cave。观测序列在被挪指令执行之前采集现场:

stp x16, x17, [sp, #-32]!     ; 一条指令完成分配与保存
adrp/add  x16, lease_slot     ; 物化观测槽地址
更新 SEQ 为奇数               ; 标记写入中
dmb ishst
写入选定 GPR/SP/NZCV/SIMD     ; x16/x17/SP 使用保存值重建
更新并 release 发布偶数 SEQ    ; 标记一帧完成
ldp x16, x17, [sp], #32       ; 临时状态结束
<relocated instruction>       ; 执行原地址语义
b anchor+4

这里的关键不是能保存多少寄存器,而是观察时刻确定:记录发生在原指令之前,一条不多、一条不少。直通形态没有观测写入;完整形态可以覆盖 31 个 GPR、SP、NZCV 与 q0–q3,观测序列按实际 payload 生成。

稳态命中没有 IPI、没有 EL0→EL1、没有单步,也不调用外部 agent。读端通过序列号判断记录是否完整;需要严格多线程快照时使用 per-thread slot,避免多个 writer 交错发布出表面合法、内部混合的数据。

2.4 执行中被打断:按 PC 判断事务走到了哪一步

异常路径曾经免费提供一件事:线程一停,寄存器和执行点天然被冻结。Cave 去掉异常以后,stp 与 ldp 之间就变成真实存在的临时事务:SP 已经改变,x16/x17 正在借用,观测记录可能只写了一半,而原指令尚未执行。

因此系统不根据发生了哪种异常猜结果,而根据精确 PC 判断事务提交到了哪一相:

可捕获 signal 原则上延迟投递:先让线程返回 Cave,用几条指令退出临时区,再在干净现场上投递。SIGKILL 不走“以后再说”的逻辑,因为发送 SIGKILL 不等于任务已经死亡;只有exit\_state、收编完成或任务消失等内核证据才能计为 quiescent。

同步 fault 同样按 PC 分诊:入口栈操作失败时跳过观测并恢复原生语义;观测 store 遇到COW 或权限老化一类可修复 fault 时回到 anchor 重试;lease 真被拆除则记录错误并进入退役;

被挪原指令自身发生 fault 时,把现场翻译回原地址再交给程序的 handler。原则只有一句:插桩自己制造的问题,不伪装成目标程序的 SIGSEGV。

2.5 lease 建立:观测缓冲不是一块裸内存

观测结果要写到用户页,但用户页会被 munmap、mremap、mprotect、madvise,也会参与 fork、COW 和 KSM。因此 Cave 不把一个用户 VA 当作永久地址,而把它包装成 lease:

安装时检查匿名、私有、可写、非共享等条件
        ↓
pin_user_pages(FOLL_WRITE) 固定目标 PFN
        ↓
权威生命周期状态留在内核;用户槽位只承载遥测数据
        ↓
fork 后父页保持 pinned,子进程获得独立副本与独立记录

App 可以篡改自己的观测槽,所以槽内任何值都不能成为释放、退休或 quiescence 的权威凭据。

最坏后果只能是遥测不可信,不能演化成内核生命周期错误。

2.6 lease 或映射将被破坏:先封入口,再寻找所有在飞线程

当 lease 即将被破坏时,恢复 anchor 原字只能阻止新的线程进入;已经在 Cave 里的线程仍可能马上执行下一条 store。因此系统进入 logical retire,并启动 full-mm rendezvous:

恢复 anchor,封死新进入
        ↓
运行线程:在其本核 IPI handler 中读取被打断的权威 PC
非运行线程:在 rq 锁保护的稳定窗口读取 task_pt_regs
        ↓
命中 Cave 的任务按相位表恢复或推进
        ↓
重新扫描,直到 mm 内不存在匹配的在飞 PC
        ↓
才允许破坏 lease 或相关观测映射

对运行线程,task/mm/cpu/pc 四元组必须相互吻合,任何错认都触发重扫;不能从另一颗 CPU读取一个瞬间的 PC 就宣布安全。对 STOPPED/TRACED 任务使用两阶段提交:锁内快照,锁外准备恢复材料,再次进入锁后逐字段复验 !on_cpu、任务状态、mm、pc、sp,全部一致才提交。

这堵住了任务已经重新运行,却仍按旧现场修寄存器的 TOCTOU。

这里也不同于 RCU 的普通宽限期:RCU 通常只需等待所有参与者越过某个 quiescent state;

Cave 必须精确证明当前 mm 内没有线程还持有指向旧 Cave 的 PC。一次误判,就可能让下一条观测 store 落到已经释放或重新映射的页上。因此它需要的是地址相关、任务相关的 in-flight证明,而不是一个全局宽限期已经过去的推断。

Cave 代码页自身的销毁属于同类问题,但不能在这里偷换成已经完成的保证。当前公开结论限定在生命周期稳定的 ELF text 页;对可能动态改权、卸载或重映射的 JIT 页,必须让代码页破坏也进入等价的 in-flight 协议,或者直接拒绝安装。这个边界会在第六节再次说明。

2.7 退役与回收:删除锚点不等于字节可以复用

Cave 有两个不同的死亡时刻:

logical retire   恢复 anchor,禁止新进入;旧 Cave 与 shadow PFN 继续保留
physical reclaim 证明所有在飞线程都已离开以后,才允许真正回收

当前实现选择更保守的策略:逻辑退休后的 Cave 保持 DORMANT 至 mm exit。页内偏移采用append-only:退休 Cave 变成 tombstone,旧偏移永不覆写;重新安装必须选择新偏移,PFN重建时旧字节仍按相同偏移复制。

这一策略换来一条非常强的不变量:

任何仍持有旧 PC 的线程,无论被延迟多久,都不会执行到另一代 Cave 的内容。

2.8 长期运行:快路径保持安静,重同步只出现在生命周期边界

rendezvous 很昂贵,但它只应该属于 lease 销毁和退役时刻。早期实现曾在目标进程的每次VMA 操作上做全量扫描;大型 Android 应用的分配器和 GC 会产生大量 madvise/munmap,结果是生命周期保护反过来污染稳态。

最终分工是不触及 lease 与 Cave 页的 VMA 操作快速放行;普通 Cave 命中零 IPI;只有装锚、拆锚以及 lease 真正被破坏时才进入同步协议。整条生命周期可以压缩成:

install → active → logical retire → full-mm drain/rendezvous → dormant/reclaim

这也是为什么 Cave 必须与 BRK/XOL 共存。Cave 处理满足证明条件的高频稳定形态;异常通道处理一次性语义、寄存器修改、不可重定位指令和失败兜底。不是一条通道包打全场,而是让每种机制只承担自己最擅长的部分。

三、从拦截再处理到执行即观测

传统动态 Hook 的中心动作是拦截。无论拦截发生在异常、trampoline、agent 回调还是翻译块边界,系统都要先把正常控制流交给另一套处理机制,再决定记录什么、怎样执行原指令、何时返回。它的能力很灵活,但每次命中都要重新支付一次控制权转移的成本。

执行即观测改变了成本发生的位置。Shadow Cave 不在每次命中时临时组织一次处理过程,而是在安装期预先完成死区证明、指令重定位、观测序列生成和发布;运行期只执行已经生成好的普通指令;退役期再集中支付 rendezvous 与回收成本。复杂性没有消失,而是从百万次重复发生的命中点,被搬到了只发生少数几次的生命周期边界。

这种移动带来三个结构性优势。

第一,稳态成本开始与实际记录多少数据相关,而不再被异常往返或通用回调框架设定一个很高的固定底价。只需直通时,Cave 可以只有被挪指令和返回分支;需要完整寄存器画像时,再按需加入对应的 store。观测负载可以伸缩,但控制流模型不需要改变。

第二,执行视图与读取视图不必再妥协。在双视图契约覆盖范围内,完整性检查读取 original,处理器却在 shadow 中经过 Cave。隐藏不再依赖修改窗口足够短或检查恰好没有发生,而是由两个物理版本各自保持逻辑自洽。Cave 不是把痕迹擦得更干净,而是让这段插桩代码从一开始就不属于数据观察者所在的世界。

第三,失败边界变得可以定义。不满足 Cave 执行条件或需要动态修改寄存器的锚点,明确回到BRK/XOL;signal 和 fault 按 PC 相位收敛;lease 销毁通过 rendezvous 证明无在飞线程;旧 Cave通过 tombstone 避免代际复用。系统不要求 Cave 覆盖所有情况,而要求每一种情况都有清楚的通道、提交点和失败出口。所以 Shadow Cave 不是优化后的 Hook。更准确地说,它弃用的是每次命中都先拦截、再分派这一稳态模型:安装时把观测编入 shadow 执行世界,命中时直接执行,生命周期结束时再进行统一收敛。BRK/XOL 仍然存在,但它从默认主路径退到动态语义与异常兜底层。在双视图契约覆盖的外部读取路径中,目标页仍呈现未经修改的原始代码;在处理器取指路径中,锚点已经成为通往 Cave 的普通分支。控制流完成原指令重定位、状态观测和结果发布后回到anchor+4。整个稳态过程不需要异常、不需要单步、不需要每次进入内核,也不需要跨核rendezvous。

这不再只是如何把 Hook 做得更隐蔽,而是如何在同一个进程内部构造一套能够长期运行、可观测、可退役,并且对外保持原始视图的平行执行系统。

Shadow Cave 不回答拦截以后怎么办,它回答的是:能不能不拦截,让观测成为执行本身?

四、工程依据

以下结果均来自一加ACE5/骁龙8 Gen3 /6.1内核真机:

指令正确性

40 万组随机重定位对拍:accepted 399,989 / refused 11 / mismatch 0
native/cave 双基线全绿;BL/BLR 的 LR 精确等于 anchor+4
ADR/ADRP/literal/条件分支/CBZ/TBZ/BR/RET/SIMD/LL-SC 全覆盖

成本

直通形态(无观测):            增量 min 0.4 / median ≈5 / worst ≈10 ns/op
x0-x7 + NZCV:                 增量 ≈1.9 ns/op
全量 31GPR+SP+NZCV+q0-q3:     增量 ≈4.5 ns/op
→ 10⁶/s 逐次全观测 CPU 占用 ≈0.45%

并发与生命周期

在飞退休:       51.9 亿次命中 × 并发 del/重装/release_all/del_all,
sum == 次数×7 精确一致(直通语义错一条指令即改和)
lease drain:单形态最高 31.8 亿次在飞写命中,线程和值精确、进程不崩
fork × lease:父 PFN 不变 / 父 PTE 可写 / 子 PFN 不同 / 父子记录隔离
signal·fault:    异页·同页 handler、SA_NODEFER、siglongjmp、ucontext 改 PC、
SIGSTOP/CONT、SIGKILL、F1–F4 全相位矩阵通过
LL/SC:单锚 LDXR/STXR、整循环锚、四线程竞争,三形态全过

某大型 Android 应用实测

cavemap  → 输入 .so,输出可用 Cave 与锚点(PROVEN 死区图)
cavewatch→ 选一个函数,一行完成选址/注入 lease/装锚
caveprof → 周期 drain,聚合命中率与寄存器直方图

实测:libart nterp_op_monitor_enter 锚点,
交互期稳定 ~300 次/s,逐次记录被锁对象引用,
输出“当前最热锁对象”分布——在该实测工作负载下,未观察到可见功能回归或稳定性退化。

五、时序侧信道:隐身不是零时延

双视图解决的是读取者看到哪一份代码,Cave 解决的是稳态命中是否必须进入异常。

但处理器仍然会执行分支、重定位指令、观测 store、屏障和 lease 发布;这些动作可能改变缓存占用、流水线压力、内存序和跨核竞争。异常消失以后,时序信号没有消失,只是从强信号变成了低幅度背景中的微小扰动。

可以把一次观测到的时间写成一个并不神秘的分解:

T_observed = T_native
           + Δbranch
           + Δregister_observe
           + Δcache/TLB
           + Δcontention
           + noise(频率、电源、温度、调度、后台任务)

传统 BRK/XOL 的异常入口通常形成容易识别的高幅度事件;Shadow Cave 的稳态路径则主要表现为 Δbranch、少量 store 和内存屏障。单次测量很难把它从 CPU 频率变化、大小核迁移、后台录屏、GC、页面老化和系统调度中分离出来。即使能在实验室里通过大量样本得到统计差异,也不等于能在商业对抗中把它变成可靠的逐进程判定器。

这带来三个必须区分的结论:

  • 检测理论上可行。 对照 native 与 Cave 形态,结合长窗口统计、线程亲和性和负载控制,
    可以观察到分布变化。
  • 检测工程上困难。 信号强度取决于锚点形态、观测 payload、微架构、设备状态和并发背景,
    没有跨设备稳定的固定阈值。
  • 检测产品化更困难。低端设备、后台录屏、温控降频和系统抖动都可能产生同量级甚至更大的
    时延变化;把弱统计差异直接转成封禁决定,会把误报风险转化成运营事故。

因此,Shadow Cave 的目标不是不可检测,而是把检测从逻辑枚举推进到物理统计:正常 API、页表读取和代码完整性检查在双视图契约内无法直接给出结论;剩下的只能是低 SNR的侧信道观测。对于防守方而言,更合理的做法是把时序结果作为多源风险评分中的一个弱特征,而不是单独作为零误伤的判决依据。

对 Cave 自身而言,这也构成设计约束:稳态路径保持短、固定、无 IPI;昂贵 rendezvous只放在安装、退役和 lease 真正破坏的生命周期边界;观测 payload 按需选择,避免为了多记几项而把可测信号人为放大。隐身的工程含义不是让物理世界没有变化,而是让变化不再形成稳定、可复现、低成本的判定信号。

六、边界

  • 直通形态也有观测维度。
    直通形态没有临时寄存器状态,不需要恢复 barrier;
    但异步信号仍可能观察到 Cave PC——对外呈现时必须做 canonical-PC 映射
    (原指令未执行 → anchor;已执行 → anchor+4;信号 handler 通过 ucontext 读取 PC 时,不能让它看到无法归属于原程序的 Cave 地址)。
    无事务不等于无身份问题。
  • 死区证明的强度有上限。
    仅依赖直接分支扫描的死区证明不能称为绝对 PROVEN;
    jump table、BR/BLR 间接目标需要编译器/链接器控制流地图。
  • Cave 代码页自身的生命周期。
    lease 销毁有 rendezvous;Cave 所在代码页被 mprotect/munmap/mremap/写入时,
    在飞执行流同样需要覆盖。对生命周期稳定的 ELF text 页风险较低;
    对动态改权/JIT 页不能直接外推。
  • 观测者效应不为零。
    观测形态中的 store、dmb ishst、stlr 会影响缓存与内存序。
    Cave 可以做到无异常,但不能声称对一切无锁算法与数据竞争程序零干扰。
    LL/SC 四用例矩阵是当前 SoC 上的强实证,不是所有 ARM64 微架构的数学定理。
  • 多写者严格快照要 per-thread slot。
    单 writer 下 seqlock 严格;多 writer 交错存在首尾相等的不可检混合。
    默认形态是 best-effort 遥测 + torn 标记;严格场景用 TPIDR\_EL0 派生槽位。

结语

传统拦截模型的历史贡献不容否认异常帧可以保存寄存器、冻结执行点并统一 signal 与 fault 入口;运行时回调和二进制翻译则用更强的控制权换取灵活性。ptrace、uprobes、Frida、PIN,各自在自己的模型里走到了很远。

Cave 把异常往返与运行时分派从稳态路径拿掉之后,命中速度开始逼近原生。
但异常曾经免费提供的秩序,也随之消失了。

于是 Cave 真正重建的不是一段跳板,而是:原地址语义、临时寄存器事务、signal 相位、fault 翻译、lease 生命周期、多线程 quiescence、退休与代际、失败时的诚实收敛。

异常模型的代价是慢,但异常替我们冻结了世界。Cave 的速度接近原生可从跳入 Cave 的那一刻起,世界不会再替我们停下来。

真正困难的,从来不是如何跳进一个洞。

而是当所有线程、信号、页表和生命周期都仍在继续前进时,如何保证最终还能完整地走出来。

XOL 仍是在异常之后设法把原指令继续执行Shadow Cave 则让原指令与观测代码在异常之外共同执行,前者优化的是异常之后的收尾,后者改变的是命中本身

异常与回调模型追问的是拦截以后怎么办。Shadow Cave 选择的是重新划定边界——把高成本的复杂操作推到生命周期边界,把稳态路径还给普通指令。

这不是 Hook 框架的下一版,而是观测逻辑从事件模型迁入执行模型的开始。

看雪ID:mb\_iiulbilc

https://bbs.kanxue.com/user-home-864142.htm

\*本文为看雪论坛优秀文章,由 mb\_iiulbilc 原创,转载请注明来自看雪社区

9月10日【议题征集】截止

往期推荐

从 r0capture 到 eCapture:Hermes Agent 自动抓包在真实任务里的价值

Kali 下 Android Studio 无法输入中文的问题排查与解决

Intel 酷睿 CPU Management Engine 固件研究与分析逆向:实战DCI链接与ME解锁尝试

记一次入门级DexVMP分析

DirtyFrag浅析及一条新的攻击路径

球分享

球点赞

球在看

点击阅读原文查看更多


当高频观测不再经过异常路径:ShadowCave与常驻式插桩架构" 网络安全文章

当高频观测不再经过异常路径:ShadowCave与常驻式插桩架构"

本文介绍ShadowCave常驻式插桩架构,基于original/shadow双视图将观测代码发布到shadow执行视图,使高频命中不再经过异常路径,稳态命中不依赖异常、单步或跨核同步。相比传统BRK方案性能提升近两个数量级,并系统阐述死区证明、指令重定位、事务化退役等生命周期管理机制,提供完整验证结果。
覆盖车联网、信创、AI等领域,360入选工信部六大漏洞专业库技术支撑单位" 网络安全文章

覆盖车联网、信创、AI等领域,360入选工信部六大漏洞专业库技术支撑单位"

360集团入选工信部NVDB六大安全漏洞专业库技术支撑单位,实现全覆盖,其中在移动互联网APP产品安全漏洞专业库获评唯一五星级。其漏洞研究院汇聚十余万白帽人才,累计报送百万级漏洞。AI安全攻防系统图龙锋已挖掘超10000个漏洞,服务覆盖政府、金融、汽车等重点领域,推动漏洞发现迈向机器速度。
YTray:浏览器隔离与分身测试新神器" 网络安全文章

YTray:浏览器隔离与分身测试新神器"

YTray是万径安全旗下Yaklang团队开发的面向安全测试与研发联调的原生桌面应用,可为每项任务创建独立可恢复的本地浏览器实例,实现身份隔离、代理配置、插件加载与调试端口管理,数据仅存本机。文章提供四步使用指南及多角色权限测试典型场景,建议在合法授权测试中使用。
PaperCut提醒注意已遭利用的NG和MF0day漏洞" 网络安全文章

PaperCut提醒注意已遭利用的NG和MF0day漏洞"

PaperCut公司警告其NG和MF打印管理软件所有版本存在0day漏洞,已被黑客积极利用。已确认有客户遭攻击,公司发布紧急补丁并建议限制Web界面访问。提供了妥协指标如server.log中的错误信息。回顾了2023年类似漏洞被Clop和LockBit勒索软件利用的历史。建议管理员立即采取缓解措施。
评论:0   参与:  0