JavaScript中Math.random函数的伪随机性解析以及破解方法研究
字数 1464 2025-08-22 22:47:39

JavaScript中Math.random函数的伪随机性解析及破解方法研究

1. 随机性概念解析

1.1 什么是随机性

  • 随机性指事件中明显缺乏可预测性
  • 理论上,如果知道所有影响因素及其准确值,可以预测结果(如抛硬币)
  • 计算机中的随机数实际上是"伪随机"的

1.2 伪随机数生成器(PRNG)

  • 基于算法生成数字序列
  • 需要一个初始值(种子/seed)
  • 生成过程:用当前数字生成下一个数字,形成序列
  • 序列会重复,重复的长度称为"周期",周期越大算法越好
  • 应用场景:游戏(如角色初始位置生成),但不适用于密码学

2. JavaScript中的Math.random

2.1 基本特性

  • 返回大于0且小于1的浮点伪随机数
  • Chrome浏览器使用V8引擎实现该函数
  • V8是开源的,由Google维护

2.2 V8实现细节

  • 使用xorshift128+算法
  • 源代码位置:
    • GitHub: https://github.com/v8/v8/tree/7a4a6cc6a85650ee91344d0dbd2c53a8fa8dce04
    • 官方博客: https://v8.dev/blog/math-random

3. xorshift128+算法分析

3.1 算法实现代码

static inline void XorShift128(uint64_t* state0, uint64_t* state1) {
    uint64_t s1 = *state0;
    uint64_t s0 = *state1;
    *state0 = s0;
    s1 ^= s1 << 23;
    s1 ^= s1 >> 17;
    s1 ^= s0;
    s1 ^= s0 >> 26;
    *state1 = s1;
}

3.2 算法特点

  • 基于两个64位状态变量(state0和state1)
  • 主要操作:位异或(^)和位移(<<, >>)
  • 每次调用会更新这两个状态变量

4. 使用Z3求解器破解Math.random

4.1 Z3求解器简介

  • 由微软开发的SMT求解器
  • 可将问题表示为方程式并指定约束条件
  • 自动求解满足条件的变量值

4.2 破解步骤

4.2.1 准备工作

  1. 收集Math.random生成的随机数序列(至少5个)

    Array.from(Array(5), Math.random)
    

    示例序列:

    sequence = [0.6199046082820001, 0.6623637813965961, 
                0.7190181683749095, 0.06169296721449724, 
                0.915799780594273]
    
  2. 安装必要的Python库:z3, struct

4.2.2 实现破解脚本

#!/usr/bin/python3
import z3, struct, sys

# 1. 准备已知的随机数序列(逆序排列)
sequence = [0.6199046082820001, 0.6623637813965961, 
            0.7190181683749095, 0.06169296721449724, 
            0.915799780594273]
sequence = sequence[::-1]

# 2. 创建Z3求解器和状态变量
solver = z3.Solver()
se_state0, se_state1 = z3.BitVecs("se_state0 se_state1", 64)

# 3. 模拟xorshift128+算法
for i in range(len(sequence)):
    se_s1 = se_state0
    se_s0 = se_state1
    se_state0 = se_s0
    se_s1 ^= se_s1 << 23
    se_s1 ^= z3.LShR(se_s1, 17)  # 使用逻辑右移
    se_s1 ^= se_s0
    se_s1 ^= z3.LShR(se_s0, 26)
    se_state1 = se_s1
    
    # 4. 将随机数转换为64位表示并提取尾数
    float_64 = struct.pack("d", sequence[i] + 1)
    u_long_long_64 = struct.unpack("<Q", float_64)[0]
    mantissa = u_long_long_64 & ((1 << 52) - 1)
    
    # 5. 添加约束条件
    solver.add(int(mantissa) == z3.LShR(se_state0, 12))

# 6. 检查并求解
if solver.check() == z3.sat:
    model = solver.model()
    states = {}
    for state in model.decls():
        states[state.__str__()] = model[state]
    
    # 7. 预测下一个随机数
    state0 = states["se_state0"].as_long()
    u_long_long_64 = (state0 >> 12) | 0x3FF0000000000000
    float_64 = struct.pack("<Q", u_long_long_64)
    next_sequence = struct.unpack("d", float_64)[0]
    next_sequence -= 1
    
    print(next_sequence)

4.3 关键点说明

  1. 状态变量:使用两个64位变量(se_state0, se_state1)表示算法内部状态

  2. 算法模拟:完全按照V8的xorshift128+算法实现

  3. 浮点数处理

    • JavaScript的Math.random返回[0,1)范围内的双精度浮点数
    • 使用struct模块处理浮点数的二进制表示
    • 提取52位尾数作为约束条件
  4. 约束条件

    • 将已知随机数的尾数与算法状态的移位结果关联
    • 使用逻辑右移(LShR)而非算术右移
  5. 预测下一个数

    • 求解得到当前状态后,计算下一个状态
    • 将状态转换为符合Math.random输出格式的浮点数

5. 实际应用与限制

5.1 应用场景

  • 预测游戏中的随机事件
  • 分析依赖Math.random的安全机制
  • 研究伪随机数生成器的特性

5.2 限制条件

  • 需要连续获取多个(≥5)随机数样本
  • 仅适用于使用xorshift128+算法的环境(如Chrome浏览器)
  • 不适用于经过加密处理的随机数生成器

6. 防御措施

如果应用需要不可预测的随机数:

  • 使用密码学安全的随机数生成器(如Web Crypto API)
  • 不依赖Math.random进行安全相关的操作
  • 考虑使用外部熵源增加随机性

7. 总结

本文详细解析了JavaScript中Math.random函数的实现原理,基于V8引擎的xorshift128+算法,并提供了使用Z3求解器破解该伪随机数生成器的方法。通过收集少量连续的随机数样本,可以重建算法内部状态并预测后续输出。这一技术揭示了伪随机数生成器在特定条件下的可预测性,强调了在安全敏感场景中使用适当随机数源的重要性。

JavaScript中Math.random函数的伪随机性解析及破解方法研究 1. 随机性概念解析 1.1 什么是随机性 随机性指事件中明显缺乏可预测性 理论上,如果知道所有影响因素及其准确值,可以预测结果(如抛硬币) 计算机中的随机数实际上是"伪随机"的 1.2 伪随机数生成器(PRNG) 基于算法生成数字序列 需要一个初始值(种子/seed) 生成过程:用当前数字生成下一个数字,形成序列 序列会重复,重复的长度称为"周期",周期越大算法越好 应用场景:游戏(如角色初始位置生成),但不适用于密码学 2. JavaScript中的Math.random 2.1 基本特性 返回大于0且小于1的浮点伪随机数 Chrome浏览器使用V8引擎实现该函数 V8是开源的,由Google维护 2.2 V8实现细节 使用xorshift128+算法 源代码位置: GitHub: https://github.com/v8/v8/tree/7a4a6cc6a85650ee91344d0dbd2c53a8fa8dce04 官方博客: https://v8.dev/blog/math-random 3. xorshift128+算法分析 3.1 算法实现代码 3.2 算法特点 基于两个64位状态变量(state0和state1) 主要操作:位异或(^)和位移(< <, >>) 每次调用会更新这两个状态变量 4. 使用Z3求解器破解Math.random 4.1 Z3求解器简介 由微软开发的SMT求解器 可将问题表示为方程式并指定约束条件 自动求解满足条件的变量值 4.2 破解步骤 4.2.1 准备工作 收集Math.random生成的随机数序列(至少5个) 示例序列: 安装必要的Python库:z3, struct 4.2.2 实现破解脚本 4.3 关键点说明 状态变量 :使用两个64位变量(se_ state0, se_ state1)表示算法内部状态 算法模拟 :完全按照V8的xorshift128+算法实现 浮点数处理 : JavaScript的Math.random返回 [ 0,1)范围内的双精度浮点数 使用struct模块处理浮点数的二进制表示 提取52位尾数作为约束条件 约束条件 : 将已知随机数的尾数与算法状态的移位结果关联 使用逻辑右移(LShR)而非算术右移 预测下一个数 : 求解得到当前状态后,计算下一个状态 将状态转换为符合Math.random输出格式的浮点数 5. 实际应用与限制 5.1 应用场景 预测游戏中的随机事件 分析依赖Math.random的安全机制 研究伪随机数生成器的特性 5.2 限制条件 需要连续获取多个(≥5)随机数样本 仅适用于使用xorshift128+算法的环境(如Chrome浏览器) 不适用于经过加密处理的随机数生成器 6. 防御措施 如果应用需要不可预测的随机数: 使用密码学安全的随机数生成器(如Web Crypto API) 不依赖Math.random进行安全相关的操作 考虑使用外部熵源增加随机性 7. 总结 本文详细解析了JavaScript中Math.random函数的实现原理,基于V8引擎的xorshift128+算法,并提供了使用Z3求解器破解该伪随机数生成器的方法。通过收集少量连续的随机数样本,可以重建算法内部状态并预测后续输出。这一技术揭示了伪随机数生成器在特定条件下的可预测性,强调了在安全敏感场景中使用适当随机数源的重要性。