JS调试技巧:如何让时间和随机数“听你指挥”?升级版!
大家好,之前写过一篇JS调试技巧:如何让时间和随机数“听你指挥”?的文章,讲到了如何固定时间和随机数,让随机的结果变得固定。本篇文章分享一种升级版的方法,让固定方法不那么容易被发现。
固定随机数
上一篇文章中提到,使用 hook 方法固定随机数,如果只是单纯的固定随机数让其返回一个固定的值,在某些情况下容易被发现,一旦被检测到随机数的返回值固定不变,就会导致代码死循环或者报错。
猿人学 2024 新春题目就会检测随机数,如果我固定 0.5 的话,就会卡死浏览器,猜测应该是走了错误的分支。
这个时候,要如何让随机数的结果“固定”,但是又不“固定”呢?答案就是伪随机数。
伪随机数的生成有两种方式,一种是伪随机数数组,使用一个固定的数组来模拟,另一种就是伪随机算法。
伪随机数数组
伪随机数数组的实现方案有两种,一种是使用固定的随机数数组来模拟随机数,另一种就是使用伪随机数算法,来动态的计算随机数。一个是实时计算,另一个是提前计算然后存储起来,大同小异。
这里我使用固定的随机数数组来模拟伪随机数,首先看下之前的固定随机数的方案:
1 | // 备份原函数 |
为了能获取一些随机数用于填充数组,要先修改上面的方法,新建一个数组来存储获取到的随机数,然后运行一遍程序,填充数组后再重放即可。
1 | const originalRandom = Math.random; |
执行上面的 hook,list 数组会被填充,获取 list 数组的值并保存下来。
1 | const originalRandom = Math.random; |
修改 hook 代码后使用数组的值重放即可,后面无论如何调用随机数函数,都会获得一个固定的随机数序列。
这里会有一个问题,如果 hook 以后随机数调用的次数超出了数组的长度怎么办?
这个时候需要定义一个比较长的数组,或者使用后备方案,如果真的超出了,为了不报错就使用真实的随机数函数好了。
修改代码如下:
1 | const originalRandom = Math.random; |
在数组越界后调用真实的随机数即可。
伪随机算法
如果不想保存一个很大的数组,可以考虑使用伪随机算法,伪随机算法可以保证对于同一个种子,输出相同的随机数。举个例子:
1 | function createXorshift32(seed) { |
执行结果:
可以看到,针对相同的种子,获取的随机数是相同的。
当然这里大家也可以使用自己的伪随机算法,或者自己创造一个都可以,其实伪随机算法就是一个比较简单的计算,根据输入计算输出即可,只要结果符合随机数的范围。
还有更多伪随机算法我这里只写一个,大家可以自行搜集。
1 | function createMulberry32(seed) { |
有了伪随机算法后,更改随机数 hook 方案为以下代码即可:
1 |
|
运行结果如下:
结果符合预期。只是数据不是特别“随机”,这个就需要大家去调整伪随机算法了。
注:以上部分方案来自于零点大佬的 B 站视频(https://www.bilibili.com/video/BV1Eu4m1A7Zm/),在此感谢零点大佬的分享。
固定时间
固定时间的方案和固定随机数相同,只不过时间是递增的,也可以使用数组保存起来重放,具体代码就留给大家自行实现啦。
总结
以上就是升级版的固定随机数和时间的方案了,对于猿人学 2024 新春题目可以达到完美的固定效果,大家感兴趣可以尝试一下。
本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注公众号 (LLLibra146):
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!