# Javascript加密混淆

## **为什么要对JS代码进行保护？**

* JS代码运行于客户端。
* JS代码是公开透明的。

由于这两个原因，至使JS代码是不安全的，任何人都可以读、分析、复制、盗用，甚至篡改。因此出现了很多安全事件，典型的如：自己的原创程序代码被盗用、网站数据被篡改等等。

如果你不想让自己的代码被它人看到、不想它人了解你的代码功能，或者想降低被不怀好意的人甚至是黑客攻击。那么你应该尝试使用JS保护。

## 如何对Javascript代码进行保护？

* **使JS代码不可读**

  让攻击者无法理解代码功能，也无法篡改任何功能。
* **使JS代码不可分析**

  让攻击者不能进行动态跟踪调试。

  代码不可读之后，攻击者往往会进行动态跟踪调试，以期逆向还原出原始代码，或分析出程序功能。

## 怎么实现代码的不可读不可分析？

* 代码加密：加密后的代码看似乎杂乱，但代码在执行前需要进行解密才能执行，只要找到解密函数即可还原出原始代码，所以此方式安全系数较低；
* 虚拟机技术：可以屏蔽JS原始关键字，但兼容性较差，无法保证在多端，多浏览器下运行；
* **代码混淆：使用字符串阵列化、平展控制流、多态变异、僵尸函数等手段，使代码变的不可读不可分析，达到最终保护的目的。且不影响代码原有功能。是理想、实用的JS保护方案。**

## JavaScript加密

> 密码学有一句话： **当你采用的加密模式，使得攻击者为了破解所付出的代价 远远超过其所获得的利益之时，你的加密方案就是安全的。**

* 代码无法做到完全不可读，只能做到很难读。
* 只能增加逆向难度，从而提高山寨逆向成本。

造成这两点的原因是，你无法绕过浏览器的web inspector(下文简称WI)。有人会说在JS做黑盒，但其最终调用的还是浏览器提供的API。你是无法防止他人在你调用API时候拦截的。除非你的代码拥有比WI更高的API权限。否则都是没有用。

[**国内最知名的JS加密当属JShaman**](http://jshaman.com/guide.html)

[**Node.js加密算法库Crypto**](https://nodejs.org/api/crypto.html)

## JavaScript混淆

**脱离混淆的Javascript加密是伪命题，无论怎么加密，如果不加以混淆手段保护，都没有意义。**

如同传统软件的加壳保护，js混淆给底层的加密算法加了最基本的保障，在js层面来说，混淆和加密一定是相辅相成的。

由于js是动态指令码语言，在http中传输的就是原始码，逆向起来要比打包编译后的软体简单很多。暴露在外的程式码没有绝对的安全，但是在对抗中，精心设计的混淆程式码能够给破坏者带来不小的麻烦，也能够为防守者争取更多的时间，相对于破解来说，混淆器规则的更替成本要小得多，在高强度的攻防中，可以大大增加破解者的工作量，起到防御作用。从这个角度来讲，关键程式码进行混淆是必不可少的步骤。

JS混淆归结为三类： eval类型，hash类型，压缩类型

### [eval混淆](http://blog.w3cub.com/tools/jspacker/)

也是最早JS出现的混淆加密，据说第一天就被破解，修改一下代码，alert一下就可以破解了。

### hash混淆

* miniui 使用的\[JSA加密]\(<https://sourceforge.net/projects/jsintegration/files/tools/\\>\_ JSA-20071021/)
* fundebug使用的[javascript-obfuscator](https://obfuscator.io/)

JSA加密 和 javascript-obfuscator 的区别：

通过\[JSA加密]\(<https://sourceforge.net/projects/jsintegration/files/tools/\\>\_ JSA-20071021/)混淆后生成的代码，[beautifier](https://beautifier.io/)一下，可以发现，其实没有做什么什么修改，只是做了一些变量替换。想还原也比较简单的。这里就不拿它来做代表，也没有什么人用。

通过[javascript-obfuscator](https://obfuscator.io/)混淆后生成的代码，[beautifier](https://beautifier.io/)一下，分析一下可以发现，其实多了一个字典，所有方法变量，都有可能存在字典中，调用时先调用字典还原方法名变量再执行。其实入口都是变量的规则。

### 压缩混淆

是目前前端性能优化的常用工具，以[uglify](https://www.npmjs.com/package/uglify-js)为代表。

### js混淆器大致有两种

* 通过正则替换实现的混淆器
* 通过语法树替换实现的混淆器

第一种实现成本低，但是效果也一般，适合对混淆要求不高的场景。

第二种实现成本较高，但是更灵活，而且更安全，更适合对抗场景。基于语法层面的混淆器其实类似于编译器，基本原理和编译器类似，我们先对编译器做一些基本的介绍。

#### 编译器工作流程

简单的说，当我们读入一段字串文字（source code），词法分析器会把它拆成一个一个小的单位（token），比如数字1 是一个token, 字串'abc'是一个token等等。接下来语法分析器会把这些单位组成一颗树状结构（AST），这个树状结构就代表了token们的组成关系。比如1 + 2 就会展示成一棵加法树，左右子节点分别是token - 1 和token - 2 ，中间token表示加法。编译器根据生成的AST转换到中间程式码，最终转换成机器程式码。

对编译器更多细节感兴趣的同学可以移步龙书：[编译原理](https://book.douban.com/subject/5416783/)

#### 混淆器工作流程

编译器需要把原始码编译成中间程式码或者机器码，而我们的混淆器输出其实还是js。所以我们从语法分析之后往下的步骤并不需要。想想我们的目标是什么，是修改原有的js程式码结构，在这里面这个结构对应的是什么呢？就是AST。任何一段正确的js程式码一定可以组成一颗AST，同样，因为AST表示了各个token的逻辑关系，我们也可以通过AST反过来生成一段js程式码。所以，你只需要构造出一颗AST，就能生成任何js程式码！

#### 规则设计

知道了大致的混淆流程，最重要的环节就是设计规则。我们上面说了，我们需要生成新的AST结构意味着会生成和原始码不一样的js程式码，但是我们的混淆是不能破坏原有程式码的执行结果的，所以混淆规则必须保证是在不破坏程式码执行结果的情况下，让程式码变得更难以阅读。

具体的混淆规则各位可以自行根据需求设计，比如拆分字串、拆分阵列，增加废程式码等等。

[混淆器设计？](https://www.zhihu.com/question/22841206/answer/43999530)

参考：提供商业混淆服务的[ jscramble](https://jscrambler.com/)的混淆规则

### 混淆插件

#### [javascript-obfuscator](https://github.com/javascript-obfuscator/javascript-obfuscator)

> 一个免费和高效的JavaScript混淆器(包括ES2017)。让你的代码更难复制，防止别人窃取你的成果。这个工具是一个优秀的Web UI(并且是开源的)
>
> 这个库很像在线`JavaScript`代码压缩网站，实际上也可以做一个在线压缩代码的网站。不过他吸引我的是他的cli工具。
>
> ❗ 作者在`Github`上说了没有很多时间来维护这个项目了，使用请慎重考虑。

```js
//源码
function aa () {
	console.log('aaa')
}
aa()

//混淆后
var _cs=['\x61\x61\x61']; function _f0() { console.log(_cs[0]) } _f0()
```

#### [uglify-js](https://www.npmjs.com/package/uglify-js)

是一款JS代码处理工具，提供了压缩，混淆和代码规范化等功能。通过以下传参数，可以对js做些混淆：

```js
mangle: {
	toplevel: true, // — 混淆在最高作用域中声明的变量名（默认false）
	eval: true,	// - 混淆在eval 或 with作用域出现的变量名（默认false）
	properties: {} //**警告：**这能会搞崩你的代码。混淆属性名跟混淆变量名不一样，是相互独立的。会混淆对象所有可见的属性名，除了DOM属性名和JS内置的类名。
}
```

> uglify-js可以对变量名、属性、方法名进行混淆，无法对字符串进行混淆。

#### [closure-webpack-plugin](https://www.npmjs.com/package/closure-webpack-plugin)

与uglify-js类似。有对应webpack 4\webpack 3的插件。

#### [jscrambler](https://jscrambler.com/)

jscrambler是一个商业级工具，看了很多社区的评论，这个目前是最好的，需要付费。

#### [jsfuck（开源）](https://github.com/aemkei/jsfuck)

jsfuck 是一个开源的js 混淆工具，原理比较简单，其实就是通过特定的字符串加上下标定位字符，再由这些字符替换源代码，从而实现混淆。而且文件体积会受很大影响。

```js
//源码
alert('a')

//转换后
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+........
```

## 反调试

由于JavaScript自带`debugger`语法，我们可以利用死循环性的`debugger`，当页面打开调试面板的时候，无限进入调试状态。

## [把js代码转化为二进制字节码](http://www.fairysoftware.com/js_er_jing_zhi.html)

## 代码可以放置其他位置（非js文件）

放到png文件中：利用HTML Canvas 2D Context获取二进制数据的特性，可以用图片存储脚本资源。

利用HTML [Canvas 2D Context](https://www.w3.org/TR/2dcontext/)获取二进制数据的特性，可以用图片存储脚本资源。

放在css文件中：利用`content`样式能存放字符串的特性，同样可以用来存储脚本资源。

## [代码块预处理工具](https://github.com/zswang/jdists)

## 在线加密混淆工具

<https://www.jsjiami.com/> （在线）

<https://www.sojson.com/jsjiemi.html> (在线)

参考链接：

[加密基础知识四 前端JS加密传输 crypto-js](https://www.jianshu.com/p/1aed5b55ca27)

[js混淆加密，通过混淆Js程式码让别人(很难)无法还原](https://www.itread01.com/content/1546872497.html)

[移动时代的前端加密](https://github.com/smileyby/codeProtect)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lizh.gitbook.io/knowledge/research/javascript-jia-mi-hun-xiao.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
