在使用某插件的过程中,大量个性化需求不能满足,于是我有了更改源码的冲动。翻遍所有角落,只找了一份压缩混淆的 js 文件。
能否反混淆,这是本节讨论的重点。
一、场景复现
先来说说几种我们迫切需要知道源码的情况:
1.阅读源码,当然,大部分开源的代码都是可以直接查看的;
2.对某插件做个性化的需求更改,这时候你渴望看到未混淆压缩的代码;
3.为了增加代码分析的难度,混淆(obfuscate)工具被应用到了许多恶意软件(如 0day 挂马、跨站攻击等)当中。
分析人员为了掀开恶意软件的面纱,首先就得对脚本进行反混淆(deobfuscate)处理。
4.当你准备抄袭别人代码时,这个稍微有点不可描述;
本文,我们假设是在前两种场景的条件下,来探索一下 js 反混淆问题。
二、寻求方案
为了快速的解决问题,我们首先尝试一下现有方案:
http://jsnice.org/
一个简单的示例:
解析后:
是不是反混淆之后,可读性强了很多。
2.js 代码混淆站长工具
我们先从 Lodash 找一段演示代码,如下:
普通混淆后,就变成了这样:
为了测试一下如何反混淆,我们将混淆后代码拷贝到 jsnice :
可以看到,普通混淆后,代码还是可以做一些复原。
好的,我们加大力度,采用加密压缩方式。这次代码明显多了,而 jsnice 也反混淆失败了:
这段代码,地球人已经没法读懂了,乱七八糟的。那么,问题来了,加密混淆的代码真的没有办法复原吗?
三、思维突破
从上面的演示可以看出来,加密之后的混淆,已经完全无法反混淆了。
除非我们知道混淆算法,可是混淆方式不计其数,你需要知晓它的混淆方式,并制定出反混淆算法,那估计会累死。
如果我们这么想的话,那么就陷入了泥潭,甚至无法自救。那么,突破点到底在哪里呢?
众所周知,JavaScript 是解释性语言,它严重依赖游览器。不管 JavaScript 如何混淆,最终浏览器都会知道最真实的代码。所以,我们还得以浏览器为突破口。
首先,同步一下原始代码:
其次,确定源码中是否包含在 eval 中,参考代码如下:
然后,查找关键字 throw,如果有,那就成功了一大步;
最后,改动源码,让源码抛出异常,让 Eval Code 还原出真实代码;
可以看到,为了演示,我们让源代码具有某些特性,然而实际情况会远远复杂于此;
四、相关工具介绍
我们先来看一下,目前常用的混淆工具:
- YUI Compressor
- Google Closure Compiler
- UglifyJS
- JScrambler
反混淆工具:
- jsbeautifier
- JSDetox
了解更多,请参考:几种常见的JavaScript混淆和反混淆工具分析实战
五、了解混淆手段
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。
六、写在最后
JavaScript 作为一个以函数式为核心的多范式动态弱类型脚本语言,因为它的灵活性,导致了源代码在经过一些压缩工具处理后,变得极难还原。
也有可能,当我们费劲心思还原出来的代码,也许只是与源代码运行流程一致的另一套代码。当然,我们可以继续探索,发掘未知的领域。
七、热门原创文章
本文暂时没有评论,来添加一个吧(●'◡'●)