JS调试技巧:如何让时间和随机数“听你指挥”?

大家好,今天分享一个可以固定随机数和时间的方法。

随机数

在 JS逆向的时候,随机数是很常见的一个函数,很多算法和逆向代码中都用到了随机数,但是在运行抠出来的代码时,很多时候会因为随机数的原因导致浏览器和 nodejs 运行的结果不一致,但是却又不知道是因为环境被检测到了还是因为随机数导致的,这个时候就需要用到初中物理八年级学到的控制变量法了。

因为环境检测无法控制,只能控制随机数了。混淆后的代码可能无法精准找到随机数生成的位置,hook 随机数函数就是一个不错的方案。

hook 随机数

获取随机数一般会用到 Math.random 函数,hook 代码如下:

1
2
3
4
5
6
7
8
9
10
// 备份原函数
const originalRandom = Math.random;
// 重写为固定返回值
Math.random = function() {
return 0.123456789; // 替换为所需固定值
};
// 防止检测:伪装原生函数特征
Math.random.toString = function() {
return "function random() { [native code] }";
};

image-20250505201705902

运行效果如上图,随机数不再随机了,可以按需求来生成随机数。

如果需要多个不同的可控随机数的话,可以考虑使用以下方案:

1
2
3
4
5
let index = 0;
const presetValues = [0.1, 0.5, 0.9];
Math.random = function() {
return presetValues[index++ % presetValues.length];
};

image-20250505201831863

随机数会按照预定义好的数组返回对应的随机数,这样在遇到会多次调用随机数并且会判断随机数是否相等的情况下就会很有用了。

对了,获取随机数的方式不止一种,还有一种不太常用的方法就是 window.crypto.getRandomValues 方法,它的参数是一个数组,调用函数传入数组后,函数会使用随机数填充数组,然后返回填充后的数组。

hook 方法如下:

1
2
3
4
5
6
7
8
9
10
const originalGetRandomValues = window.crypto.getRandomValues;
window.crypto.getRandomValues = function(array) {
// 填充固定值(如全0数组)
array.fill(0);
return array;
};
// 防御检测
window.crypto.getRandomValues.toString = function() {
return "function getRandomValues() { [native code] }";
};

image-20250505203338352

运行效果如上图,可以自定义填充内容,例如全 0 或者全 1 完全自定义。

Date

和随机数一样,时间在 JS逆向时更加常用,基本上那些加密参数中都会有时间函数的参与,用来防止重放攻击。hook 时间函数可以排除加密逻辑中时间变量的干扰,让结果可复现,方便对比 nodejs 和浏览器的加密结果。

hook Date

hook 代码如下:

1
2
3
4
5
6
// 固定所有 Date 相关方法的返回值
Date.now = function() { return 1746449425147; };
Date.parse = function() { return 1746449425147; };
Date.prototype.getTime = function() { return 1746449425147; };
Date.prototype.valueOf = function() { return 1746449425147; };
Date.prototype.toString = function() { return "Fri Sep 02 2024 12:00:00 GMT+0800"; }; // 固定字符串格式

image-20250505203757635

hook 的时候别忘了原型链上的相关方法,如上图所示。还有别忘了重写相关函数的 toString 方法,防止被检测到函数被篡改。

1
2
3
4
5
6
Date.now.toString = function() {
return "function now() { [native code] }";
};
Date.prototype.getTime.toString = function() {
return "function getTime() { [native code] }";
};

实战效果

使用猿人学练习平台的第六题实战看看,hook 随机数和时间函数以后,重复执行加密函数。

image-20250505204531815

image-20250505204543096

可以看到即使是刷新页面,重新执行加密方法,加密的结果也是相同的,因为这道题用到了随机数,如果不 hook 随机数函数的话,每次加密的结果都是不一样的。

总结

以上就是固定随机数和时间函数的两种方法了,后续大家在扣代码的时候,遇到类似的情况可以直接拿来使用。

本文章首发于个人博客 LLLibra146’s blog

本文作者:LLLibra146

更多文章请关注公众号 (LLLibra146):LLLibra146

版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!

本文链接
https://blog.d77.xyz/archives/2345f7fa.html