大家好,今天来分享一种防止 JS 对象属性被修改的方法。

冻结对象

在 JS 逆向中,有时候会遇到 console.xxx 被覆盖的情况,如果遇到基本上对应的方法都会被重写成一个空方法,也就是说无论执行什么方法,都不会输出任何内容。这个时候无论是插桩或者 hook 都无法获得日志输出,对于调试很不利,非常影响效率。而且除非我们能一步步的找到被覆盖的地方,否则可能再也无法输出日志了。

Object.freeze

这个时候我们可以考虑使用 Object.freezefreeze 方法可以冻结一个对象,一个冻结的对象再也不行被修改,不能向这个对象添加新的属性,不能删除已有的属性,不能修改该对象已有属性的值,对这个对象的原型的修改也会被禁止。

在非严格模式,对被冻结对象的属性修改操作会成功,但是实际上修改是无效的,在严格模式中,对于被冻结的属性的修改会抛出 TypeError 错误。

实战

理论知识铺垫完了,下面来实战试试。以下题目节选自猿人学内部练习平台 web 题第 21 题。

这是一道套了轻度 OB 混淆的 webpack 的题目,题目不算难,主要是它覆盖了 console 的几个方法,先来看看效果:

image-20250225220034242

无论调用什么方法,都无法正常输出日志。

解决方案就是使用 Object.freeze 来冻结 console 对象,但是记得在网页自身的 JS 代码执行前进行冻结,不然冻结的就是被覆盖后的方法了。

油猴脚本

为了可以在网页 JS 代码执行前进行冻结,需要使用油猴脚本,新建一个油猴脚本,写入以下代码:

// ==UserScript==
// @name        demo
// @namespace   Violentmonkey Scripts
// @match       https://www.python-spider.com/*
// @grant       none
// @version     1.0
// @author      -
// @description 2025/2/25 21:20:14
// @run-at      document-start
// ==/UserScript==
(function () {
Object.freeze(console);
console.log('freeze success!')
})();

默认情况下油猴脚本会在网页的 JS 代码执行后再执行,所以别忘了添加一行注释:@run-at document-start,它会让油猴脚本在网页的 JS 代码执行前先执行我们的油猴脚本。

hook 操作也是一样的原理,要先执行 hook 逻辑,再执行正常的代码,才能正确执行 hook 操作。

保存油猴脚本,刷新网页看看:
image-20250225221537158

可以看到,打印出来了冻结成功的消息,并且后续在使用 log 方法也是可以正常使用的。

可能有小伙伴会问,为什么 debug 方法还是没有输出呢,那是因为 Chrome 控制台的默认日志级别是 info,将其改成 debug 即可正常显示所有的日志。

image-20250225221708564

image-20250225221719293

将详细勾选上,即可正常显示所有的日志了。

查找覆盖点

到了这里其实问题就解决了,但是如果冻结操作被检测到了怎么办?

因为冻结操作是可以通过 Object.isFrozen 方法检测的,如果传入被冻结的对象就会返回 true。或者在严格模式下,如果混淆代码中对 console 对象进行属性覆盖操作,就会报错,错误可能会被检测到进而走不同的分支。

这个时候,我们可能真的需要详细分析混淆代码了,找到方法是在哪里被冻结的,然后针对性的 pass 掉检测代码。那如何找到覆盖点呢?可以尝试一下直接输出被冻结对象,然后从下面的路径中查找被覆盖后的函数:

image-20250225223405997

可以看到现在 log 方法被覆盖了,但是可以看到覆盖点是 webpack 这个 JS 中的某个方法,点击一下跳过去打个断点看看:

image-20250225224607160

image-20250225224705011

断点断在了空函数的位置,果然被替换成了空函数,然后找找调用栈是哪里调用了 _0x56b1f7 函数(绿色箭头),是在 _0x291e45 这个函数(红色箭头)里面,通过各种赋值,最终在循环中将 console 的各个方法覆盖成了空函数,见下图箭头所指:

image-20250225225203659

既然找到了覆盖的位置,就可以针对性的反混淆了,可以直接将 _0x291e45 这个函数注释掉试试,让其不再被调用,也就不会覆盖 console 对象了。感兴趣的小伙伴可以自行尝试,我就不演示了。

总结

以上冻结操作中,Object.freeze 是浅冻结,如果被冻结的对象中还有其他的对象,其他的对象是可以被修改的。需要深冻结的话,需要递归冻结每个类型为对象的属性,用到了递归要注意不要死循环,也可以使用开源库来实现。这里就不演示了,有需求的小伙伴可以自行尝试一下。

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

本文作者:LLLibra146

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

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

本文链接https://blog.d77.xyz/archives/91eb85a9.html


LLLibra146
35 声望7 粉丝

会修电脑的程序员