说说 JS 反混淆与常用手段

说说 JS 反混淆与常用手段

在使用某插件的过程中,大量个性化需求不能满足,于是我有了更改源码的冲动。翻遍所有角落,只找了一份压缩混淆的 js 文件

能否反混淆,这是本节讨论的重点。

一、场景复现

说说 JS 反混淆与常用手段

先来说说几种我们迫切需要知道源码的情况:

1.阅读源码,当然,大部分开源的代码都是可以直接查看的;

2.对某插件做个性化的需求更改,这时候你渴望看到未混淆压缩的代码;

3.为了增加代码分析的难度,混淆(obfuscate)工具被应用到了许多恶意软件(如 0day 挂马、跨站攻击等)当中。

分析人员为了掀开恶意软件的面纱,首先就得对脚本进行反混淆(deobfuscate)处理。

4.当你准备抄袭别人代码时,这个稍微有点不可描述;

本文,我们假设是在前两种场景的条件下,来探索一下 js 反混淆问题。

二、寻求方案

说说 JS 反混淆与常用手段

为了快速的解决问题,我们首先尝试一下现有方案:

http://jsnice.org/
说说 JS 反混淆与常用手段

一个简单的示例:

说说 JS 反混淆与常用手段

解析后:

说说 JS 反混淆与常用手段

是不是反混淆之后,可读性强了很多。

2.js 代码混淆站长工具

我们先从 Lodash 找一段演示代码,如下:

说说 JS 反混淆与常用手段

普通混淆后,就变成了这样:

说说 JS 反混淆与常用手段

为了测试一下如何反混淆,我们将混淆后代码拷贝到 jsnice :

说说 JS 反混淆与常用手段

可以看到,普通混淆后,代码还是可以做一些复原。

好的,我们加大力度,采用加密压缩方式。这次代码明显多了,而 jsnice 也反混淆失败了:

说说 JS 反混淆与常用手段

这段代码,地球人已经没法读懂了,乱七八糟的。那么,问题来了,加密混淆的代码真的没有办法复原吗?

三、思维突破

从上面的演示可以看出来,加密之后的混淆,已经完全无法反混淆了。

除非我们知道混淆算法,可是混淆方式不计其数,你需要知晓它的混淆方式,并制定出反混淆算法,那估计会累死。

如果我们这么想的话,那么就陷入了泥潭,甚至无法自救。那么,突破点到底在哪里呢?

众所周知,JavaScript 是解释性语言,它严重依赖游览器。不管 JavaScript 如何混淆,最终浏览器都会知道最真实的代码。所以,我们还得以浏览器为突破口。

首先,同步一下原始代码:

说说 JS 反混淆与常用手段

其次,确定源码中是否包含在 eval 中,参考代码如下:

说说 JS 反混淆与常用手段

然后,查找关键字 throw,如果有,那就成功了一大步;

最后,改动源码,让源码抛出异常,让 Eval Code 还原出真实代码;

可以看到,为了演示,我们让源代码具有某些特性,然而实际情况会远远复杂于此;

四、相关工具介绍

说说 JS 反混淆与常用手段

我们先来看一下,目前常用的混淆工具:

  • YUI Compressor
  • Google Closure Compiler
  • UglifyJS
  • JScrambler

反混淆工具:

  • jsbeautifier
  • JSDetox

了解更多,请参考:几种常见的JavaScript混淆和反混淆工具分析实战

五、了解混淆手段

说说 JS 反混淆与常用手段

1.base62 编码,其最明显的特征是生成的代码以

eval(function(p,a,c,k,e,r)) 开头;

看到这里,上述站长工具的加密方式,就不难理解了。

这类混淆的关键思想在于将需要执行的代码进行一次编码,在执行的时候还原出浏览器可执行的合法的脚本,然后执行之。

看上去和可执行文件的加壳有那么点类似。Javascript 提供了将字符串当做代码执行(evaluate)的能力,可以通过 Function 构造器、eval、setTimeout、setInterval 将字符串传递给 js 引擎进行解析执行。

无论代码如何进行变形,其最终都要调用一次 eval 等函数。

解密的方法不需要对其算法做任何分析,只需要简单地找到这个最终的调用,改为 console.log 或者其他方式,将程序解码后的结果按照字符串输出即可。

2.隐写术

严格说这不能称之为混淆,只是将 js 代码隐藏到了特定的介质当中。

如通过最低有效位(LSB)算法嵌入到图片的 RGB 通道、隐藏在图片 EXIF 元数据、隐藏在 HTML 空白字符等。

比如这个耸人听闻的议题:《一张图片黑掉你》在图片中嵌入恶意程序,正是使用了最低有效位平面算法。结合 HTML5 的 canvas 或者处理二进制数据的 TypeArray,脚本可以抽取出载体中隐藏的数据(如代码)。

隐写的方式同样需要解码程序和动态执行,所以破解的方式和前者相同,在浏览器上下文中劫持替换关键函数调用的行为,改为文本输出即可得到载体中隐藏的代码。

3.复杂表达式

代码混淆不一定会调用 eval,也可以通过在代码中填充无效的指令来增加代码复杂度,极大地降低可读性。

Javascript 中存在许多称得上丧心病狂的特性,这些特性组合起来,可以把原本简单的字面量(Literal)、成员访问(MemberExpression)、函数调用(CallExpression)等代码片段变得难以阅读。

在 js 中可以找到许多这样互逆的运算,通过使用随机生成的方式将其组合使用,可以把简单的表达式无限复杂化。

深入了解,请移步使用 estools 辅助反混淆 Javascript

六、写在最后

说说 JS 反混淆与常用手段

JavaScript 作为一个以函数式为核心的多范式动态弱类型脚本语言,因为它的灵活性,导致了源代码在经过一些压缩工具处理后,变得极难还原。

也有可能,当我们费劲心思还原出来的代码,也许只是与源代码运行流程一致的另一套代码。当然,我们可以继续探索,发掘未知的领域。

1.

2.

3.

说说 JS 反混淆与常用手段


分享到:


相關文章: