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 关键攻击面
-
ClassOps 函数指针:覆盖可导致代码执行
- 位于只读内存区域,需构造完整伪造结构
- 通过任意写修改 group_ 中的 clasp_ 指针
-
ArrayBuffer 数据指针:控制后可实现任意内存读写
- 结合 TypedArray 视图实现精确内存操作
- 相比普通数组,避免浮点数转换问题
4.2 利用技术
- 类型混淆:利用类型表示机制缺陷
- 内存破坏:通过对象内部指针实现任意地址读写
- 逻辑漏洞:利用引擎内部逻辑错误
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 调试技巧
- 查找对象内存:
gdb -p $(pidof js)
find 0x11223344
- 检查对象结构:
x/4xg 0x7f7f01b90120 # 查看对象头
tel 0x00007f7f01bb18d0 4 # 遍历shape链表
- 检查数据内容:
x/s 0x7f7f01bae6a8 # 查看字符串
x/2xg 0x7f6181a69600 # 查看ArrayBuffer数据
6. 总结
SpiderMonkey 的关键数据结构设计体现了 JavaScript 引擎的核心机制:
- JSValue:统一的动态类型表示方案
- JSObject:复杂的属性管理架构
- TypedArray:高性能二进制数据处理能力
理解这些数据结构对于:
- 深入理解 JavaScript 运行机制
- 进行引擎级性能优化
- 分析和利用安全漏洞
都具有重要意义。