SpiderMonkey 中的一些数据结构
字数 1323 2025-08-03 16:49:33

SpiderMonkey 引擎中的关键数据结构解析

1. JSValue 值表示机制

在 JavaScript 中,变量可以动态改变类型,SpiderMonkey 使用 JSValue/jsval 结构来统一表示各种类型的值。

1.1 JSValue 的内存布局

  • 64位结构:前17位表示类型标记(tag),低47位存储实际值
  • 类型标记:通过移位操作与值组合存储

示例:

a = [0x11223344, "STRING", 0x44332211, true]

内存中表示为:

0x7f8e531980d0: 0xfff8800011223344  // int 0x11223344
0x7f8e531980d8: 0xfffb7f8e531ae6a0  // string "STRING"
0x7f8e531980e0: 0xfff8800044332211  // int 0x44332211
0x7f8e531980e8: 0xfff9000000000001  // boolean true

1.2 类型定义

js/public/Value.h 中定义的类型系统:

enum JSValueType : uint8_t {
    JSVAL_TYPE_DOUBLE = 0x00,
    JSVAL_TYPE_INT32 = 0x01,
    JSVAL_TYPE_BOOLEAN = 0x02,
    JSVAL_TYPE_UNDEFINED = 0x03,
    JSVAL_TYPE_NULL = 0x04,
    JSVAL_TYPE_MAGIC = 0x05,
    JSVAL_TYPE_STRING = 0x06,
    JSVAL_TYPE_SYMBOL = 0x07,
    JSVAL_TYPE_PRIVATE_GCTHING = 0x08,
    JSVAL_TYPE_OBJECT = 0x0c
};

1.3 标记生成机制

JS_ENUM_HEADER(JSValueTag, uint32_t) {
    JSVAL_TAG_MAX_DOUBLE = 0x1FFF0,
    JSVAL_TAG_INT32 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
    // 其他类型类似
};

标记通过移位操作生成:

JSVAL_SHIFTED_TAG_INT32 = (((uint64_t)JSVAL_TAG_INT32) << JSVAL_TAG_SHIFT)

2. JSObject 对象结构

JavaScript 对象在内存中表示为 JSObject 类的实例,NativeObject 是其重要子类。

2.1 NativeObject 基本结构

class NativeObject {
    js::GCPtrObjectGroup group_;
    void* shapeOrExpando_;
    js::HeapSlot* slots_;
    js::HeapSlot* elements_;
};

2.2 group_ 成员

group_ 指向 ObjectGroup 对象,包含:

const Class* clasp_;        // 共享的类定义
GCPtr<TaggedProto> proto_;  // 共享的原型
JS::Realm* realm_;          // 所属的域
ObjectGroupFlags flags_;    // 标志位
void* addendum_ = nullptr;  // 附加信息
Property** propertySet = nullptr; // 属性集合

2.3 Class 结构

struct Class {
    JS_CLASS_MEMBERS(js::ClassOps, FreeOp);
    const ClassSpec* spec;
    const ClassExtension* ext;
    const ObjectOps* oOps;
};

其中 ClassOps 包含关键函数指针:

struct ClassOps {
    JSAddPropertyOp addProperty;
    JSDeletePropertyOp delProperty;
    JSEnumerateOp enumerate;
    JSNewEnumerateOp newEnumerate;
    JSResolveOp resolve;
    JSMayResolveOp mayResolve;
    FinalizeOp finalize;
    JSNative call;
    JSHasInstanceOp hasInstance;
    JSNative construct;
    JSTraceOp trace;
};

2.4 属性存储机制

对象属性通过 shape_slots_ 配合存储:

  • slots_:存储属性值的数组
  • shape_:链表结构,存储属性名和对应 slots 数组的索引

示例对象:

obj = {}
obj.blahblah = 0x55667788
obj.strtest = "TESTSTRING"

内存布局:

group_    -> 0x00007f7f01b8a310
shape_    -> 0x00007f7f01bb18d0 (链表头)
slots_    -> 0x00007f7f01844ec0 (值数组)
elements_ -> 0x000000000174a490

2.5 元素存储

数组元素存储在 elements_ 指向的区域:

obj[0] = 0x11223344
obj[1] = 0x33557711

内存中包含元数据:

struct {
    uint32_t flags;
    uint32_t initializedLength; // 已初始化元素数
    uint32_t capacity;          // 分配容量
    uint32_t length;            // 数组长度
}

3. TypedArray 类型数组

3.1 ArrayBuffer 结构

ArrayBuffer 表示固定长度的二进制数据缓冲区:

arrbuf = new ArrayBuffer(0x100);
uint32view = new Uint32Array(arrbuf);
uint16view = new Uint16Array(arrbuf);

3.2 内存布局

ArrayBuffer 对象:

group_     -> 0x00007f618108a8b0
shape_     -> 0x00007f61810b1a38
slots_     -> 0x0000000000000000
elements_  -> 0x000000000174a490
data ptr   -> 0x00003fb0c0d34b00 (实际右移1位)
length     -> 0xfff8800000000100
first view -> 0xfffe7f6183d003a0
flags      -> 0xfff8800000000008

3.3 视图结构

TypedArray 视图额外包含:

  • 底层 ArrayBuffer 指针
  • 长度
  • 数据偏移量
  • 直接数据指针(性能优化)

Uint32Array 视图示例:

ArrayBuffer -> 0xfffe7f618109a080
length      -> 0xfff8800000000040
offset      -> 0xfff8800000000000
data ptr    -> 0x00007f6181a69600

3.4 数据指针特性

数据指针通过 setPrivate 设置:

void setPrivate(void* ptr) {
    asBits_ = uintptr_t(ptr) >> 1;
}

TypedArray 直接操作原始内存数据,不经过 NaN-boxing,适合精确内存操作。

4. 安全与利用考量

4.1 关键攻击面

  1. ClassOps 函数指针:覆盖可导致代码执行

    • 位于只读内存区域,需构造完整伪造结构
    • 通过任意写修改 group_ 中的 clasp_ 指针
  2. ArrayBuffer 数据指针:控制后可实现任意内存读写

    • 结合 TypedArray 视图实现精确内存操作
    • 相比普通数组,避免浮点数转换问题

4.2 利用技术

  1. 类型混淆:利用类型表示机制缺陷
  2. 内存破坏:通过对象内部指针实现任意地址读写
  3. 逻辑漏洞:利用引擎内部逻辑错误

5. 调试与分析方法

5.1 构建调试环境

hg clone http://hg.mozilla.org/mozilla-central spidermonkey
cp configure.in configure && autoconf2.13
mkdir build_DBG.OBJ
cd build_DBG.OBJ
../configure --disable-debug --disable-optimize
make
cd dist/bin/
./js

5.2 GDB 调试技巧

  1. 查找对象内存:
gdb -p $(pidof js)
find 0x11223344
  1. 检查对象结构:
x/4xg 0x7f7f01b90120  # 查看对象头
tel 0x00007f7f01bb18d0 4  # 遍历shape链表
  1. 检查数据内容:
x/s 0x7f7f01bae6a8  # 查看字符串
x/2xg 0x7f6181a69600 # 查看ArrayBuffer数据

6. 总结

SpiderMonkey 的关键数据结构设计体现了 JavaScript 引擎的核心机制:

  1. JSValue:统一的动态类型表示方案
  2. JSObject:复杂的属性管理架构
  3. TypedArray:高性能二进制数据处理能力

理解这些数据结构对于:

  • 深入理解 JavaScript 运行机制
  • 进行引擎级性能优化
  • 分析和利用安全漏洞
    都具有重要意义。
SpiderMonkey 引擎中的关键数据结构解析 1. JSValue 值表示机制 在 JavaScript 中,变量可以动态改变类型,SpiderMonkey 使用 JSValue/jsval 结构来统一表示各种类型的值。 1.1 JSValue 的内存布局 64位结构 :前17位表示类型标记(tag),低47位存储实际值 类型标记 :通过移位操作与值组合存储 示例: 内存中表示为: 1.2 类型定义 在 js/public/Value.h 中定义的类型系统: 1.3 标记生成机制 标记通过移位操作生成: 2. JSObject 对象结构 JavaScript 对象在内存中表示为 JSObject 类的实例,NativeObject 是其重要子类。 2.1 NativeObject 基本结构 2.2 group_ 成员 group_ 指向 ObjectGroup 对象,包含: 2.3 Class 结构 其中 ClassOps 包含关键函数指针: 2.4 属性存储机制 对象属性通过 shape_ 和 slots_ 配合存储: slots_ :存储属性值的数组 shape_ :链表结构,存储属性名和对应 slots 数组的索引 示例对象: 内存布局: 2.5 元素存储 数组元素存储在 elements_ 指向的区域: 内存中包含元数据: 3. TypedArray 类型数组 3.1 ArrayBuffer 结构 ArrayBuffer 表示固定长度的二进制数据缓冲区: 3.2 内存布局 ArrayBuffer 对象: 3.3 视图结构 TypedArray 视图额外包含: 底层 ArrayBuffer 指针 长度 数据偏移量 直接数据指针(性能优化) Uint32Array 视图示例: 3.4 数据指针特性 数据指针通过 setPrivate 设置: TypedArray 直接操作原始内存数据,不经过 NaN-boxing,适合精确内存操作。 4. 安全与利用考量 4.1 关键攻击面 ClassOps 函数指针 :覆盖可导致代码执行 位于只读内存区域,需构造完整伪造结构 通过任意写修改 group_ 中的 clasp_ 指针 ArrayBuffer 数据指针 :控制后可实现任意内存读写 结合 TypedArray 视图实现精确内存操作 相比普通数组,避免浮点数转换问题 4.2 利用技术 类型混淆 :利用类型表示机制缺陷 内存破坏 :通过对象内部指针实现任意地址读写 逻辑漏洞 :利用引擎内部逻辑错误 5. 调试与分析方法 5.1 构建调试环境 5.2 GDB 调试技巧 查找对象内存: 检查对象结构: 检查数据内容: 6. 总结 SpiderMonkey 的关键数据结构设计体现了 JavaScript 引擎的核心机制: JSValue :统一的动态类型表示方案 JSObject :复杂的属性管理架构 TypedArray :高性能二进制数据处理能力 理解这些数据结构对于: 深入理解 JavaScript 运行机制 进行引擎级性能优化 分析和利用安全漏洞 都具有重要意义。