Skip to content

JS混淆

更新: 2025/2/24 字数: 0 字 时长: 0 分钟

服务器接收 JS 加密参数或返回 JS 加密数据,就一定会把 JS 文件给到客户端执行,所以客户端一定是可以看到 JS 代码。但安全公司,为了防止 JS 代码被他人使用或分析,会对 JS 的代码进行混淆,就是在不改变 JS 代码逻辑的情况下,对变量名称、函数名称等进行改变,让 JS 代码的阅读性变得很差,代码混淆越厉害,还原的难度就越高,以此来达到保护代码的目的。

20200617231838

混淆简介

ob混淆

ob混淆:**[全称是 obfuscator,它是一款免费、开源的 JavaScript 混淆工具,用以保护你的核心 JS 代码不轻易被破解,是目前常见的混淆之一。**代码经过 ob 混淆后的代码由五部分组成:

  • 第一段(特征):第一行可以看到明显的一个数组;

20211014180722

  • 第二段(特征):通常会有一个数组移位方法(有内存泄露风险、建议不格式化);

20220306010804

  • 第三段:解密函数(有内存泄露风险、建议不格式化);

  • 第四段:实际代码+控制流平坦化(整体ob的强度几乎完全取决于这段的代码强度,这里面是加密前的逻辑);

  • 第五段:控制流平坦化+无限debugger自执行函数+死代码注入。一般情况下不会有业务逻辑

反混淆

针对混淆的 JS 代码,我们有四种处理方法:

  1. 硬刚混淆的 JS 代码,混淆的 JS 代码也是可以执行的代码,因此我们可以通过调试一步步还原执行流程,只不过这个过程比较艰辛,会掉不少头发;
  2. **使用反混淆的线上工具(地址:http://tool.yuanrenxue.com/deobfuscator)**,通常反混淆的效果不理想,如果混淆的 JS 代码中多了些空格、分号、冒号是反混淆不了的,另外如果 JS 代码比较多,反混淆的时间也会比较长。
  3. 使用 AST 反混淆 JS 代码,AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构。语法树不是某一种编程语言独有的,JavaScript、Python、Java、Golang 等几乎所有编程语言都有语法树。通过 AST 解析,我们可以像童年时拆解玩具一样,深入了解 JS 这台机器的各个零部件,然后重新按照我们自己的意愿来组装,所以不要问,ob 混淆、sojson 如何破解,这些东西只是一层壳,破解强度完全取决于网站作者写的代码强度,但如果你掌握了 AST,将这些代码进行还原,则可以大大降低硬刚 JS 代码的难度,非常的nice!
  4. **使用浏览器自带的设置,可以一定程度的反混淆。**先来看看没打开设置之前的状态,全局搜索加密字段 password 结果如下:只有 login-handler-kz-ums-3.0-min.js 文件是最有可能与加密字段 password 相关的。

20230624220213

但是点开后发现是 eval 函数和一段混淆的字符串,这种混淆后的字符串,被执行后,会生成一个虚拟文件(VM + 数字结尾),此时我们通过全局搜索也是搜索不到的。

20230624220459

现在我们打开浏览器的设置,流程如下:开发者工具——Sources——Settings——Preferences——打勾”Search in anonymous and content scripts“选项(在匿名脚本和内容脚本中搜索,此时就会搜索所有已加载的脚本)。

20230618032913

20230618033141

设置好以后,再次进行关键字的全局搜索,会多出几个文件,其中就包括 VM192 文件,里面就是执行 eval 后的虚拟文件的代码,这样可以一定程度上的反混淆:

20230624220727

猿人学第1题

题目难度:简单

进入题目后按 F12 打开开发者工具,出现 setInterval 定时器函数,直接禁用断点,然后就可以继续执行了,F5 刷新一下:

20210903172610

但为了后面打断点不受 debugger 影响,我们还需要在 debugger 这里打断点,然后在蓝色断点位置,点击鼠标右键,选择 Edit breakpoint:

20210926175109

输入属性 false:

20210926175250

蓝色断点变黄:

20210926175305

定位到网页的数据来源:

20210903172949

查看请求头和请求参数,总体上没有什么特别,但有一个参数m是加密的,结合经验判断m的值组成形式为:加密参数丨时间戳

20210916174342

接下就是定位加密参数的生成方式,点击左侧的 Initiator 选项,它主要是标记请求是由哪个对象或进程发起的(请求源),重点关注里面的 request 请求,显示从一个名称为 VM73951 的文件的第 6 行代码发送了当前请求,点击后面的地址:

20210926172517

跳转到了该文件的第 6 行,可以看到文件的内容不那么直观了,代码进行了一定的编码,熟悉字符编码格式的人就能看出来,这其实就是将一部分字符进行了 utf-8 编码,另一部分字符进行了 unicode 编码。

20210926172824

好在网上有对这类简单的编码还原的工具(地址:http://tool.yuanrenxue.com/deobfuscator),我们可以将编码内容直接粘贴过去进行还原,得到更加容易阅读的代码:

20210926173643

其实看着还原后的代码格式化后,进行折叠,可以看到两部分几乎一样的 Ajax 请求代码:

20210928163629

在 Ajax 请求中有 successcompleteerror 这三个字段,分别代表请求成功执行、不管是否成功请求都执行、请求不成功执行:

20210928164346

**然而这三部分,我们都不需要关心,因为爬虫只模拟请求的参数和过程。**因此我们可以将这三部分干掉,这下看代码就清清爽爽了:

20210928164811

**这里有一个 window.url = '/api/match/1' 赋值过程,因此可以将 window.url 替换为 /api/match/1;还有一个 window.page 根据经验就是访问的页码数,可以暂时给个定值;除此之外还有 window.f 是一个我们未知的变量,经过全局搜索以后未发现给该变量赋值,就暂时先放下。**将整个代码优化替换后,得到了更加简化的样子,但其中还有一个 oo0O0 函数我们未知:

20210929124811

oo0O0 定义赋值后其在输出栏中打印出来,可以看到结果函数内容,点击后跳转到名称为 1 的文件当中的该函数位置:

20210928181752

oo0O0 函数格式化后拷贝出来,还是按照上面的步骤,该打印的打印,该替换的替换,不用关注函数内部实现了怎样的功能,在函数的最后给出了我们想要的东西:

20210929142033

**在 oo0O0 函数最后返回了空,也就是上面 oo0O0(_0x2268f9.toString()) 的值为空,主要就是 window.f 这个值,但这个值又是在 oo0O0 函数内部所定义,且受到时间戳的影响。**分析到这里,我们就可以做最后一波优化了,将 window 全局对象都给去掉,对代码进行简化如下:

20210929154959

猿人学第2题

难度:简单

访问网址获取任务,在 Network 里面的 Fetch/XHR 选项中定位到了该网页数据的来源请求:

20211013113316

多次访问前面 3 页的页面,发现一个规律,如果访问之间的间隔时间稍微长一点,下次访问就要求强制访问 loginfo 页面,**说明该页面设置的 Cookie 的过期时间较短。**分析比较请求头参数,结合经验得出初步接结论:Cookie 的加密参数为 m,其中 m 的值有两部分组成,前半部分为加密值,后半部分为时间戳相关的参数。

20211013113721

回到 Fiddler 抓包工具,看看所抓到的数据包:在每次请求 loginfo 页面时,前面都会有两次请求,我们分别来看看两次请求分别返回了什么。

20211013114442

第一次请求,可以看到返回了一段混淆后的 JS 代码,注意这个时候加密参数 m 的值还是上一次请求的值

20211013114710

第二次请求,看里面的内容就是这道题所要使用的前端代码,分析作用不大。但这个时候加密参数 m 的值已经发生改变,那就只能说明浏览器加载了第一次请求返回的混淆的 JS 代码,从而改变了 m 的值。

20211013114822

我们把第一次请求返回的混淆 JS 拷贝出来,将去掉首尾的 <script> 标签,使用前面给的ob反混淆工具进行反混淆:

20211013174644

警告

该题的 JS 代码过长,该工具可能只会返回部分的代码,因此最好分段反混淆代码,最后拼接。

反编码后的第一行可以看到明显的一个数组,这就是 ob 混淆的常见特性:

20211014180722

再将完整的反混淆后的代码格式化,美化,折叠,得到如下代码:当中有一个名称为 setInterval ,这是个定时函数,后面的 timeout 参数给的 4000,意味着每 4 秒调用一次该函数:

20211014180721

**我们搜索关键词 cookie 发现在 447 行出现了 m 并且后面加上了一个字符 |,再结合前面的 document["cookie"] 基本可以判定该 m 就是 cookie 里面的加密参数了。**结合请求中的 m 的值进行比较可以得出下面关系:

浏览器请求中:m=73321de886f7efd22801e07ec312cdc6|1634197698000
js代码中:document["cookie"] = "m" + _0x4589e9() + "=" + _0x18960b(_0x2f947f) + "|" + _0x2f947f + "; path=/";
结论:
_0x4589e9():返回为空
_0x18960b(_0x2f947f):返回加密的部分
_0x2f947f:返回时间戳的部分

20211014181342