V8基础内容梳理
看到一个不错的V8基础内容介绍,exploitreversing.com/2025/01/22/… ,想到可以写一个简单的内容梳理。
Chrome架构介绍
chrome是多进程架构,毕竟开一个网页那么多任务,单进程会很慢。
- Browser Process:负责管理整个浏览器的生命周期。处理 UI(地址栏、书签、前进/后退按钮等)。管理其他进程的创建与销毁。负责网络请求(通过 Network Service)、存储(如 Cookie、本地存储)、安全策略等。
- Renderer Process:负责渲染网页内容(HTML、CSS)。执行网页中的 JavaScript 代码。(V8)处理 DOM 操作、事件处理、样式计算等。每个标签页(或站点)通常运行在独立的 Renderer Process 中(出于安全和隔离考虑,采用站点隔离 Site Isolation 策略)。
- Plugin and Extension Process:用于运行 NPAPI/PPAPI 插件(如旧版 Flash)。扩展本质上是用 HTML/CSS/JS 编写的“微型网页”。扩展的后台脚本(background scripts)、内容脚本(content scripts)等运行在独立的 Renderer Process中(有时与网页共享,有时隔离)。也会运行V8。
- GPU Process:处理所有与图形加速相关的任务(如 3D 变换、WebGL、视频解码、Canvas 渲染等)。将渲染指令发送给 GPU,提高图形性能并隔离 GPU 驱动崩溃风险。
- Utility Process:执行各种“辅助性”任务,多为短期操作。
尽管 Chrome 启动了多个进程(Browser、Renderer、GPU、Utility 等),但只有 Browser Process 拥有较高的系统权限,负责协调全局资源(如文件访问、网络连接、窗口管理等)。
所有其他进程(Renderer、GPU、Utility、Extension 等)都运行在严格限制的沙箱(Sandbox)环境中,其特点包括:
- 权限受限:无法直接访问文件系统、网络、设备或用户数据。
- 隔离性强:即使某个 Renderer Process 被恶意代码攻破(例如通过浏览器漏洞),攻击者也无法直接读取用户文件或控制系统。
- 依赖 Browser Process 代理:当沙箱进程需要执行特权操作(如保存文件、发起网络请求),必须通过进程间通信(IPC, Inter-Process Communication) 向 Browser Process 发起请求,由 Browser Process 在验证安全策略后代为执行。
V8 是 Chrome 中负责高性能解析、编译和执行 JavaScript 与 WebAssembly 代码的核心引擎,运行于沙箱化的渲染进程中,是现代 Web 应用流畅运行的关键基石。
JS执行流程:Ignition (解释器) → Sparkplug (非优化编译器) → Maglev (轻量优化编译器) → Turbofan (深度优化编译器)
WebAssembly 代码走独立编译路径(Liftoff 快速编译器 + TurboFan 优化),不经过 Ignition/Sparkplug/Maglev。
1. Ignition(解释器)
- 作用:快速启动执行,生成字节码(bytecode)并解释执行。
- 优点:启动快、内存占用低。
- 所有 JS 代码首先由 Ignition 执行。
- 在执行过程中,V8 会收集类型反馈(type feedback) 和调用频率等信息。
2. Sparkplug(快速编译器,2021 年引入)
- 作用:将 Ignition 的字节码直接编译为未经优化的机器码,不依赖类型反馈。
- 目标:减少 Ignition 解释执行的开销,提升中等热度函数的性能,同时避免 TurboFan 的高编译成本。
- 特点:
- 编译速度极快(比 TurboFan 快 10 倍以上)。
- 生成的代码性能优于 Ignition,但不如优化后的 TurboFan。
- 触发条件:函数被多次调用(但尚未热到需要 TurboFan)。
3. Maglev(轻量级优化编译器,2023 年正式启用)
- 作用:介于 Sparkplug 和 TurboFan 之间的中等优化编译器。
- 目标:以较低的编译开销获得比 Sparkplug 更好的性能,缓解 TurboFan 的压力。
- 特点:
- 利用 Ignition 收集的类型反馈进行简单优化(如内联缓存、常量传播)。
- 编译速度比 TurboFan 快,代码质量优于 Sparkplug。
- 适用场景:中等热度、不适合 TurboFan(如编译成本过高)的函数。
- 注意:Maglev 并非对所有函数都启用,目前主要用于提升 Web 应用的整体响应速度。
4. TurboFan(深度优化编译器)
- 作用:对“热点函数”(hot functions)进行高度优化的机器码生成。
- 依赖:丰富的类型反馈和运行时 profile 数据。
- 优化手段:内联、死代码消除、类型特化、逃逸分析等。
- 缺点:编译耗时长、内存开销大。
- 触发条件:函数被频繁调用(如循环内、高频事件处理器)。
TurboFan 是优化编译器,也是类型混淆漏洞的高发区(因其依赖类型反馈,若反馈错误会导致错误优化,如错误的类型假设 → 越界访问)。
Maglev 虽然较新,但也引入了新的攻击面(如其简化版的类型推断逻辑可能存在漏洞)。
Ignition 字节码本身也可能成为攻击目标(如字节码 handler 漏洞)。
V8内存管理基础概念
Pointer Tagging:为了更好地利用空间,V8会用指针的最低位来进行标识。如果是0则代表一个小整数,如果是1则表示这是一个堆对象指针。
指针压缩:V8为了节省空间,指针只记录基于区域基地址的偏移。
主要元素类型:SMI_ELEMENTS (仅包含小整数)、DOUBLE_ELEMENTS (仅包含浮点数)、ELEMENTS (包含其他类型,如字符串、对象)。
Map
为了增加访问速度,JS为具有相同属性名和顺序的对象创建一个共享的Map。
Map 结构包含了对象的动态类型、大小、属性描述符数组 (DescriptorArray)、原型指针等元信息。
当给一个对象添加新属性时,V8 会创建一个新的 Map(称为过渡),并建立从旧 Map 到新 Map 的链接。这形成了一个Map 过渡链。
对象属性
- Fast Properties:对于属性结构稳定的对象,属性值直接存储在对象内部或一个紧凑的数组中,通过索引快速访问。
- Slow Properties:对于频繁增删属性的对象,属性会以字典形式存储,访问较慢但修改灵活。
- In-object properties:部分属性可以直接存储在对象自身的内存空间内,访问速度最快。
在 V8 漏洞利用中(如类型混淆、越界读写、OOB 等),通常需要:
- 稳定地布局堆内存(heap grooming);
- 精确控制对象内存布局,以便通过越界访问篡改相邻对象的字段(如
Map指针、元素指针等); - 构造可靠的
addrof/fakeobj原语。
而 Fast Properties 正好提供了可预测、紧凑、连续的内存布局,使其成为理想目标。
Backing Store
Backing store 是 ArrayBuffer 底层用于实际存储二进制数据的内存区域,JavaScript 代码通过 ArrayBuffer 对象间接访问它。
在旧版 V8 中,ArrayBuffer 的 backing store 可以通过 ArrayBuffer.prototype.transfer() 或类型混淆被重定向,实现任意地址读写,backing store是老版本写poc常用的跳板。
现在 V8 已将 backing store 指针移入 External Pointer Table,并通过句柄访问。
即使你能篡改 ArrayBuffer 对象内部的指针字段,也无法直接控制其指向任意地址——必须先破坏外部指针表或伪造合法句柄。
V8垃圾回收
V8 使用分代式垃圾回收。新创建的对象在 新生代 (New Space),存活时间长的对象会被晋升到 老生代 (Old Space)。
目前是采用用 Scavenge 算法。
- 使用 semi-space 结构(From/To 空间)。
- 活跃对象从 From 复制到 To,死亡对象直接丢弃。
- 优点:速度快、无碎片;缺点:内存利用率仅 50%。
- 适合处理大量短命对象(符合“弱分代假说”)。
V8沙箱
除了操作系统级别的进程沙箱,V8 自身也引入了堆内沙箱(In-Process Sandbox),专门防御内存破坏漏洞在 Renderer 进程内部的利用。
传统的内存破坏漏洞(如越界读写)可以被用来篡改对象的 Map 或指针,从而实现任意读写(addrof/fakeobj 原语),最终执行任意代码。V8 沙箱旨在从根本上缓解这类攻击。
核心思路:将整个 V8 堆(包括对象、代码等)放入一个受保护的、隔离的 1TB 虚拟地址空间(即沙箱)中。
实现机制:
- 沙箱化指针 (Sandboxed Pointers):V8 堆内部的对象间引用不再使用原始的 64 位指针,而是使用 40 位偏移量(相对于沙箱基地址)。这确保了任何指针篡改都只能在沙箱内部进行,无法直接指向沙箱外的任意地址。
- 指针表 (Pointer Tables):对于必须存在于沙箱外部的对象(如 DOM 节点、ArrayBuffer 的后备存储),V8 使用外部指针表 (External Pointer Table)。沙箱内的对象只持有指向该表的句柄 (Handle)(一个带类型的索引)。访问时,V8 会通过句柄查表,并进行类型检查,防止类型混淆。
- 可信空间 (Trusted Space):一些关键的、不可变的 V8 内部对象(如字节码)被放入一个单独的“可信空间”,并通过可信指针表 (Trusted Pointer Table) 进行访问,进一步隔离。
- 代码指针沙箱化 (Code Pointer Sandboxing):函数指针也被放入代码指针表 (Code Pointer Table),防止攻击者通过篡改函数指针来劫持控制流(如 JIT-ROP)。