[天友网]一些网站加密和混淆的技巧

当我们我们在爬取网站的时刻,经常会遇到林林总总类似加密的情形,好比说:

  某个网站的 URL 带有一些看不太懂的长串加密参数,要抓取就必须要明白这些参数是怎么组织的,否则我们连完整的 URL 都组织不出来,更不用说爬取了。

  剖析某个网站的 Ajax 接口的时刻,可以看到接口的一些参数也是加密的,或者 Request Headers 内里也可能带有一些加密参数,若是不知道这些参数的详细组织逻辑就没法直接用程序来模拟这些 Ajax 请求。

  翻看网站的 JavaScript 源代码,可以发现许多压缩了或者看不太懂的字符,好比 JavaScript 文件名被编码,JavaScript 的文件内容都压缩成几行,JavaScript 变量也被修改成单个字符或者一些十六进制的字符,导致我们欠好容易凭据 JavaScript 找出某些接口的加密逻辑。

  这些情形呢,基本上都是网站为了珍爱其自己的一些数据不被容易抓取而接纳的一些措施,我们可以把它归类为两大类:

  接口加密手艺

  JavaScript 压缩、混淆和加密手艺。

  这一节课我们就来领会下这三类手艺的实现原理。

  数据珍爱

  当今大数据时代,数据已经变得越来越主要,网页和 App 现在是主流的数据载体,若是其数据的接口没有设置任何珍爱措施,在爬虫工程师解决了一些基本的反爬如封 IP、验证码的问题之后,那么数据照样可以被轻松抓取到。

  那么,有没有可能在接口或 JavaScript 层面也加上一层防护呢?可以。

  接口加密手艺

  网站运营商首先想到防护措施可能是对某些数据接口举行加密,好比说对某些 URL 的一些参数加上校验码或者把一些 id 信息举行编码,使其变得难以阅读或组织;或者对某些接口请求加上一些 token、sign 等署名,这样这些请求发送到服务器时,服务器会通过客户端发来的一些请求信息以及双方约定好的秘钥等来对当前的请求举行校验,若是校验通过,才返回对应数据效果。

  好比说客户端和服务端约定一种接口校验逻辑,客户端在每次请求服务端接口的时刻都市附带一个 sign 参数,这个 sign 参数可能是由当前时间信息、请求的 URL、请求的数据、装备的 ID、双方约定好的秘钥经由一些加密算法组织而成的,客户端会实现这个加密算法组织 sign,然后每次请求服务器的时刻附带上这个参数。服务端会凭据约定好的算法和请求的数据对 sign 举行校验,若是校验通过,才返回对应的数据,否则拒绝响应。

  JavaScript 压缩、混淆和加密手艺

  接口加密手艺看起来的确是一个不错的解决方案,但单纯依赖它并不能很好地解决问题。为什么呢?

  对于网页来说,其逻辑是依赖于 JavaScript 来实现的,JavaScript 有如下特点:

  JavaScript 代码运行于客户端,也就是它必须要在用户浏览器端加载并运行。

  JavaScript 代码是公然透明的,也就是说浏览器可以直接获取到正在运行的 JavaScript 的源码。

  由于这两个缘故原由,至使 JavaScript 代码是不平安的,任何人都可以读、剖析、复制、盗用,甚至窜改。

  以是说,对于上述情形,客户端 JavaScript 对于某些加密的实现是很容易被找到或模拟的,领会了加密逻辑后,模拟参数的组织和请求也就是易如反掌了,以是若是 JavaScript 没有做任何层面的珍爱的话,接口加密手艺基本上对数据起不到什么防护作用。

  若是你不想让自己的数据被容易获取,不想他人领会 JavaScript 逻辑的实现,或者想降低被不怀好意的人甚至是黑客攻击。那么你我们就需要用到 JavaScript 压缩、混淆和加密手艺了。

  这里压缩、混淆、加密手艺简述如下:

  代码压缩:即去除 JavaScript 代码中的不需要的空格、换行等内容,使源码都压缩为几行内容,降低代码可读性,固然同时也能提高网站的加载速率。

  代码混淆:使用变量替换、字符串阵列化、控制流平展化、多态变异、僵尸函数、调试珍爱等手段,使代码变地难以阅读和剖析,到达最终珍爱的目的。但这不影响代码原有功效。是理想、适用的 JavaScript 珍爱方案。

  代码加密:可以通过某种手段将 JavaScript 代码举行加密,转成人无法阅读或者剖析的代码,如将代码完全抽象化加密,如 eval 加密。另外另有更壮大的加密手艺,可以直接将 JavaScript 代码用 C/C++ 实现,JavaScript 挪用其编译后形成的文件来执行响应的功效,如 Emscripten 另有 WebAssembly。

  下面我们对上面的手艺划分予以先容。

  接口加密手艺

  数据一样平常都是通过服务器提供的接口来获取的,网站或 App 可以请求某个数据接口获取到对应的数据,然后再把获取的数据展示出来。

  但有些数据是对照名贵或私密的,这些数据一定是需要一定层面上的珍爱。以是差别接口的实现也就对应着差别的平安防护级别,我们这里来总结下。

  完全开放的接口

  有些接口是没有设置任何防护的,谁都可以挪用和接见,而且没有任何时空限制和频率限制。任何人只要知道了接口的挪用方式就能无限制地挪用。

  这种接口的平安性是异常异常低的,若是接口的挪用方式一旦泄露或被抓包获取到,任何人都可以无限制地对数据举行操作或接见。此时若是接口内里包罗一些主要的数据或隐私数据,就能容易被窜改或窃取了。

  接口参数加密

  为了提升接口的平安性,客户端会和服务端约定一种接口校验方式,一样平常来说会使用到种种加密和编码算法,如 Base64、Hex 编码,MD5、AES、DES、RSA 等加密。

  好比说客户端和服务器双方约定一个 sign 用作接口的署名校验,其天生逻辑是客户端将 URL Path 举行 MD5 加密然后拼接上 URL 的某个参数再举行 Base64 编码,最后获得一个字符串 sign,这个 sign 会通过 Request URL 的某个参数或 Request Headers 发送给服务器。服务器接收到请求后,对 URL Path 同样举行 MD5 加密,然后拼接上 URL 的某个参数,也举行 Base64 编码也获得了一个 sign,然后比对天生的 sign 和客户端发来的 sign 是否是一致的,若是是一致的,那就返回准确的效果,否则拒绝响应。这就是一个对照简朴的接口参数加密的实现。若是有人想要挪用这个接口的话,必须要界说好 sign 的天生逻辑,否则是无法正常挪用接口的。

  以上呢就是一个基本的接口参数加密逻辑的实现。

  固然上面的这个实现思绪对照简朴,这里还可以增添一些时间戳信息增添时效性判断,或增添一些非对称加密进一步提高加密的庞大水平。但不管怎样,只要客户端和服务器约定好了加密和校验逻辑,任何形式加密算法都是可以的。

  这里要实现接口参数加密就需要用到一些加密算法,客户端和服务器一定也都有对应的 SDK 实现这些加密算法,如 JavaScript 的 crypto-js,Python 的 hashlib、Crypto 等等。

  但照样如上文所说,若是是网页的话,客户端实现加密逻辑若是是用 JavaScript 来实现,其源代码对用户是完全可见的,若是没有对 JavaScript 做任何珍爱的话,是很容易弄清楚客户端加密的流程的。

  因此,我们需要对 JavaScript 行使压缩、混淆、加密的方式来对客户端的逻辑举行一定水平上的珍爱。

  JavaScript 压缩、混淆、加密

  下面我们再来先容下 JavaScript 的压缩、混淆和加密手艺。

  JavaScript 压缩

  这个异常简朴,JavaScript 压缩即去除 JavaScript 代码中的不需要的空格、换行等内容或者把一些可能公用的代码举行处置实现共享,最后输出的效果都压缩为几行内容,代码可读性变得很差,同时也能提高网站加载速率。

  若是仅仅是去除空格换行这样的压缩方式,实在几乎是没有任何防护作用的,由于这种压缩方式仅仅是降低了代码的直接可读性。若是我们有一些格式化工具可以轻松将 JavaScript 代码变得易读,好比行使 IDE、在线工具或 Chrome 浏览器都能还原格式化的代码。

  现在主流的前端开发手艺大多都市行使 Webpack 举行打包,Webpack 会对源代码举行编译和压缩,输出几个打包好的 JavaScript 文件,其中我们可以看到输出的 JavaScript 文件名带有一些不规则字符串,同时文件内容可能只有几行内容,变量名都是一些简朴字母示意。这其中就包罗 JavaScript 压缩手艺,好比一些公共的库输出成 bundle 文件,一些挪用逻辑压缩和转义成几行代码,这些都属于 JavaScript 压缩。另外其中也包罗了一些很基础的 JavaScript 混淆手艺,好比把变量名、方式名替换成一些简朴字符,降低代码可读性。

  但整体来说,JavaScript 压缩手艺只能在很小的水平上起到防护作用,要想真正提高防护效果还得依赖 JavaScript 混淆和加密手艺。

  JavaScript 混淆

  JavaScript 混淆是完全是在 JavaScript 上面举行的处置,它的目的就是使得 JavaScript 变得难以阅读和剖析,大大降低代码可读性,是一种很适用的 JavaScript 珍爱方案。

  JavaScript 混淆手艺主要有以下几种:

  变量混淆

  将带有含意的变量名、方式名、常量名随机变为无意义的类乱码字符串,降低代码可读性,如转成单个字符或十六进制字符串。

  字符串混淆

  将字符串阵列化集中放置、并可举行 MD5 或 Base64 加密存储,使代码中不泛起明文字符串,这样可以制止使用全局搜索字符串的方式定位到入口点。

  属性加密

  针对 JavaScript 工具的属性举行加密转化,隐藏代码之间的挪用关系。

  控制流平展化

  打乱函数原有代码执行流程及函数挪用关系,使代码逻变得杂乱无序。

  僵尸代码

  随机在代码中插入无用的僵尸代码、僵尸函数,进一步使代码杂乱。

  调试珍爱

  基于调试器特征,对当前运行环境举行磨练,加入一些强制调试 debugger 语句,使其在调试模式下难以顺遂执行 JavaScript 代码。

  多态变异

  使 JavaScript 代码每次被挪用时,将代码自身即马上自动发生变异,变化为与之前完全差别的代码,即功效完全稳定,只是代码形式变异,以此杜绝代码被动态剖析调试。

  锁定域名

  使 JavaScript 代码只能在指定域名下执行。

  反格式化

  若是对 JavaScript 代码举行格式化,则无法执行,导致浏览器假死。

  特殊编码

  将 JavaScript 完全编码为人不可读的代码,如表情符号、特殊示意内容等等。

  总之,以上方案都是 JavaScript 混淆的实现方式,可以在差别水平上珍爱 JavaScript 代码。

  在前端开发中,现在 JavaScript 混淆主流的实现是 javascript-obfuscator 这个库,行使它我们可以异常方便地实现页面的混淆,它与 Webpack 结合起来,最终可以输出压缩和混淆后的 JavaScript 代码,使得可读性大大降低,难以逆向。

  下面我们会先容下 javascript-obfuscator 对代码混淆的实现,领会了实现,那么自然我们就对混淆的机理有了加倍深刻的熟悉。

  javascript-obfuscator 的官网地址为:https://obfuscator.io/,其官方先容内容如下:

  

A free and efficient obfuscator for JavaScript (including ES2017). Make your code harder to copy and prevent people from stealing your work. ”

  它是支持 ES8 的免费、高效的 JavaScript 混淆库,它可以使得你的 JavaScript 代码经由混淆后难以被复制、盗用,混淆后的代码具有和原来的代码一模一样的功效。

  怎么使用呢?首先,我们需要安装好 Node.js,可以使用 npm 下令。

  新建一个文件夹,好比 js-obfuscate,然后进入该文件夹,初始化事情空间:

 npm init 

  这里会提醒我们输入一些信息,建立一个 package.json 文件,这就完成了项目初始化了。

  接下来我们来安装 javascript-obfuscator 这个库:

 



 npm install --save-dev javascript-obfuscator

  接下来我们就可以编写代码来实现混淆了,如新建一个 main.js 文件,内容如下:


 <code>const code = `<br>let x = '1' + 1<br>console.log('x', x)<br>`</code>



 <code>const options = {<br>compact: false,<br>controlFlowFlattening: true<br>}</code>



<code>const obfuscator = require('javascript-obfuscator')<br>function obfuscate(code, options) {<br>return obfuscator.obfuscate(code, options).getObfuscatedCode()<br>}<br>console.log(obfuscate(code, options))</code>

  在这里我们界说了两个变量,一个是 code,即需要被混淆的代码,另一个是混淆选项,是一个 Object。接下来我们引入了 javascript-obfuscator 这库,然后界说了一个方式,传入 code 和 options,来获取混淆后的代码,最后控制台输出混淆后的代码。

  代码逻辑对照简朴,我们来执行一下代码:

  

node main.js

  输出效果如下:

 <code>var _0x53bf = ['log'];<br>(function (_0x1d84fe, _0x3aeda0) {<br>var _0x10a5a = function (_0x2f0a52) {<br>while (--_0x2f0a52) {<br>_0x1d84fe['push'](_0x1d84fe['shift']());<br>}<br>};<br>_0x10a5a(++_0x3aeda0);<br>}(_0x53bf, 0x172));<br>var _0x480a = function (_0x4341e5, _0x5923b4) {<br>_0x4341e5 = _0x4341e5 - 0x0;<br>var _0xb3622e = _0x53bf[_0x4341e5];<br>return _0xb3622e;<br>};<br>let x = '1' + 0x1;<br>console[_0x480a('0x0')]('x', x);</code>

  看到了吧,那么简朴的两行代码,被我们混淆成了这个样子,实在这里我们就是设定了一个「控制流扁平化」的选项。

  整体看来,代码的可读性大大降低,也大大加大了 JavaScript 调试的难度。

  好,那么我们来随着 javascript-obfuscator 走一遍,就能详细知道 JavaScript 混淆到底有若干方式了。

  代码压缩

  这里 javascript-obfuscator 也提供了代码压缩的功效,使用其参数 compact 即可完成 JavaScript 代码的压缩,输出为一行内容。默认是 true,若是界说为 false,则混淆后的代码会分行显示。

 <code>const code = `<br>let x = '1' + 1<br>console.log('x', x)<br>`<br>const options = {<br>compact: false<br>}</code>

  若是不设置 compact 或把 compact 设置为 true,效果如下:



  let x = '1' + 0x1;
console['log']('x', x);

 <code>var _0x151c=['log'];(function(_0x1ce384,_0x20a7c7){var _0x25fc92=function(_0x188aec){while(--_0x188aec){_0x1ce384['push'](_0x1ce384['shift']());}};_0x25fc92(++_0x20a7c7);}(_0x151c,0x1b7));var _0x553e=function(_0x259219,_0x241445){_0x259219=_0x259219-0x0;var _0x56d72d=_0x151c[_0x259219];return _0x56d72d;};let x='1'+0x1;console[_0x553e('0x0')]('x',x);</code>

  可以看到单行显示的时刻,对变量名举行了进一步的混淆和控制流扁平化操作。

  变量名混淆

  变量名混淆可以通过设置 identifierNamesGenerator 参数实现,我们通过这个参数可以控制变量名混淆的方式,如 hexadecimal 则会替换为 16 进制形式的字符串,在这里我们可以设定如下值:

  hexadecimal:将变量名替换为 16 进制形式的字符串,如0xabc123

  mangled:将变量名替换为通俗的简写字符,如abc等。

  该参数默以为 hexadecimal。

  我们将该参数修改为 mangled 来试一下:

 <code>const code = `<br>let hello = '1' + 1<br>console.log('hello', hello)<br>`<br>const options = {<br>compact: true,<br>identifierNamesGenerator: 'mangled'<br>}</code>

  运行效果如下:

 <code>var a=['hello'];(function(c,d){var e=function(f){while(--f){c['push'](c['shift']());}};e(++d);}(a,0x9b));var b=function(c,d){c=c-0x0;var e=a[c];return e;};let hello='1'+0x1;console['log'](b('0x0'),hello);</code>

  可以看到这里的变量命名都酿成了ab等形式。

  若是我们将 identifierNamesGenerator 修改为 hexadecimal 或者不设置,运行效果如下:

 <code>var _0x4e98=['log','hello'];(function(_0x4464de,_0x39de6c){var _0xdffdda=function(_0x6a95d5){while(--_0x6a95d5){_0x4464de['push'](_0x4464de['shift']());}};_0xdffdda(++_0x39de6c);}(_0x4e98,0xc8));var _0x53cb=function(_0x393bda,_0x8504e7){_0x393bda=_0x393bda-0x0;var _0x46ab80=_0x4e98[_0x393bda];return _0x46ab80;};let hello='1'+0x1;console[_0x53cb('0x0')](_0x53cb('0x1'),hello);</code>

  可以看到选用了 mangled,其代码体积会更小,但 hexadecimal 其可读性会更低。

  另外我们还可以通过设置 identifiersPrefix 参数来控制混淆后的变量前缀,示例如下:

 const code = `
let hello = '1' + 1
console.log('hello', hello)
`
const options = {
identifiersPrefix: 'germey'
}

 

  运行效果:

 <code>var germey_0x3dea=['log','hello'];(function(_0x348ff3,_0x5330e8){var _0x1568b1=function(_0x4740d8){while(--_0x4740d8){_0x348ff3['push'](_0x348ff3['shift']());}};_0x1568b1(++_0x5330e8);}(germey_0x3dea,0x94));var germey_0x30e4=function(_0x2e8f7c,_0x1066a8){_0x2e8f7c=_0x2e8f7c-0x0;var _0x5166ba=germey_0x3dea[_0x2e8f7c];return _0x5166ba;};let hello='1'+0x1;console[germey_0x30e4('0x0')](germey_0x30e4('0x1'),hello);</code>

  可以看到混淆后的变量前缀加上了我们自界说的字符串 germey。

  另外 renameGlobals 这个参数还可以指定是否混淆全局变量和函数名称,默以为 false。示例如下:

<code>const code = `<br>var $ = function(id) {<br>return document.getElementById(id);<br>};<br>`<br>const options = {<br>renameGlobals: true<br>}</code>

  运行效果如下:

 <code>var _0x4864b0=function(_0x5763be){return document['getElementById'](_0x5763be);};</code>

  可以看到这里我们声明晰一个全局变量$,在 renameGlobals 设置为 true 之后,$这个变量也被替换了。若是后文用到了这个$工具,可能就会有找不到界说的错误,因此这个参数可能导致代码执行不通。

  若是我们不设置 renameGlobals 或者设置为 false,效果如下:

 <code>var _0x239a=['getElementById'];(function(_0x3f45a3,_0x583dfa){var _0x2cade2=function(_0x28479a){while(--_0x28479a){_0x3f45a3['push'](_0x3f45a3['shift']());}};_0x2cade2(++_0x583dfa);}(_0x239a,0xe1));var _0x3758=function(_0x18659d,_0x50c21d){_0x18659d=_0x18659d-0x0;var _0x531b8d=_0x239a[_0x18659d];return _0x531b8d;};var $=function(_0x3d8723){return document[_0x3758('0x0')](_0x3d8723);};</code>

  可以看到,最后照样有$的声明,其全局名称没有被改变。

  字符串混淆

  字符串混淆,即将一个字符串声明放到一个数组内里,使之无法被直接搜索到。我们可以通过控制 stringArray 参数来控制,默以为 true。

  我们还可以通过 rotateStringArray 参数来控制数组化后效果的的元素顺序,默以为 true。

  还可以通过 stringArrayEncoding 参数来控制数组的编码形式,默认不开启编码,若是设置为 true 或 base64,则会使用 Base64 编码,若是设置为 rc4,则使用 RC4 编码。

  还可以通过 stringArrayThreshold 来控制启用编码的概率,局限 0 到 1,默认 0.8。

  示例如下:

 <code>const code = `<br>var a = 'hello world'<br>`<br>const options = {<br>stringArray: true,<br>rotateStringArray: true,<br>stringArrayEncoding: true, // 'base64' or 'rc4' or false<br>stringArrayThreshold: 1,<br>}</code>

  运行效果如下:

 <code>var _0x4215=['aGVsbG8gd29ybGQ='];(function(_0x42bf17,_0x4c348f){var _0x328832=function(_0x355be1){while(--_0x355be1){_0x42bf17['push'](_0x42bf17['shift']());}};_0x328832(++_0x4c348f);}(_0x4215,0x1da));var _0x5191=function(_0x3cf2ba,_0x1917d8){_0x3cf2ba=_0x3cf2ba-0x0;var _0x1f93f0=_0x4215[_0x3cf2ba];if(_0x5191['LqbVDH']===undefined){(function(){var _0x5096b2;try{var _0x282db1=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x5096b2=_0x282db1();}catch(_0x2acb9c){_0x5096b2=window;}var _0x388c14='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x5096b2['atob']||(_0x5096b2['atob']=function(_0x4cc27c){var _0x2af4ae=String(_0x4cc27c)['replace'](/=+$/,'');for(var _0x21400b=0x0,_0x3f4e2e,_0x5b193b,_0x233381=0x0,_0x3dccf7='';_0x5b193b=_0x2af4ae['charAt'](_0x233381++);~_0x5b193b&amp;&amp;(_0x3f4e2e=_0x21400b%0x4?_0x3f4e2e*0x40+_0x5b193b:_0x5b193b,_0x21400b++%0x4)?_0x3dccf7+=String['fromCharCode'](0xff&amp;_0x3f4e2e&gt;&gt;(-0x2*_0x21400b&amp;0x6)):0x0){_0x5b193b=_0x388c14['indexOf'](_0x5b193b);}return _0x3dccf7;});}());_0x5191['DuIurT']=function(_0x51888e){var _0x29801f=atob(_0x51888e);var _0x561e62=[];for(var _0x5dd788=0x0,_0x1a8b73=_0x29801f['length'];_0x5dd788&lt;_0x1a8b73;_0x5dd788++){_0x561e62+='%'+('00'+_0x29801f['charCodeAt'](_0x5dd788)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x561e62);};_0x5191['mgoBRd']={};_0x5191['LqbVDH']=!![];}var _0x1741f0=_0x5191['mgoBRd'][_0x3cf2ba];if(_0x1741f0===undefined){_0x1f93f0=_0x5191['DuIurT'](_0x1f93f0);_0x5191['mgoBRd'][_0x3cf2ba]=_0x1f93f0;}else{_0x1f93f0=_0x1741f0;}return _0x1f93f0;};var a=_0x5191('0x0');</code>

  可以看到这里就把字符串举行了 Base64 编码,我们再也无法通过查找的方式找到字符串的位置了。

  若是将 stringArray 设置为 false 的话,输出就是这样:

 <code>var a='hello\x20world';</code>

  字符串就仍然是明文显示的,没有被编码。

  另外我们还可以使用 unicodeEscapeSequence 这个参数对字符串举行 Unicode 转码,使之加倍难以辨认,示例如下:

 <code>const code = `<br>var a = 'hello world'<br>`<br>const options = {<br>compact: false,<br>unicodeEscapeSequence: true<br>}</code>

  运行效果如下:

 <code>var _0x5c0d = ['\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64'];<br>(function (_0x54cc9c, _0x57a3b2) {<br>var _0xf833cf = function (_0x3cd8c6) {<br>while (--_0x3cd8c6) {<br>_0x54cc9c['push'](_0x54cc9c['shift']());<br>}<br>};<br>_0xf833cf(++_0x57a3b2);<br>}(_0x5c0d, 0x17d));<br>var _0x28e8 = function (_0x3fd645, _0x2cf5e7) {<br>_0x3fd645 = _0x3fd645 - 0x0;<br>var _0x298a20 = _0x5c0d[_0x3fd645];<br>return _0x298a20;<br>};<br>var a = _0x28e8('0x0');</code>

  可以看到,这里字符串被数字化和 Unicode 化,异常难以辨认。

  在许多 JavaScript 逆向的历程中,一些要害的字符串可能会作为切入点来查找加密入口。用了这种混淆之后,若是有人想通过全局搜索的方式搜索 hello 这样的字符串找加密入口,也没法搜到了。

  代码自我珍爱

  我们可以通过设置 selfDefending 参数来开启代码自我珍爱功效。开启之后,混淆后的 JavaScript 会以强制一行形式显示,若是我们将混淆后的代码举行格式化(美化)或者重命名,该段代码将无法执行。

  例如:

 <code>const code = `<br>console.log('hello world')<br>`<br>const options = {<br>selfDefending: true<br>}</code>

  运行效果如下:

 <code>var _0x26da=['log','hello\x20world'];(function(_0x190327,_0x57c2c0){var _0x577762=function(_0xc9dabb){while(--_0xc9dabb){_0x190327['push'](_0x190327['shift']());}};var _0x35976e=function(){var _0x16b3fe={'data':{'key':'cookie','value':'timeout'},'setCookie':function(_0x2d52d5,_0x16feda,_0x57cadf,_0x56056f){_0x56056f=_0x56056f||{};var _0x5b6dc3=_0x16feda+'='+_0x57cadf;var _0x333ced=0x0;for(var _0x333ced=0x0,_0x19ae36=_0x2d52d5['length'];_0x333ced&lt;_0x19ae36;_0x333ced++){var _0x409587=_0x2d52d5[_0x333ced];_0x5b6dc3+=';\x20'+_0x409587;var _0x4aa006=_0x2d52d5[_0x409587];_0x2d52d5['push'](_0x4aa006);_0x19ae36=_0x2d52d5['length'];if(_0x4aa006!==!![]){_0x5b6dc3+='='+_0x4aa006;}}_0x56056f['cookie']=_0x5b6dc3;},'removeCookie':function(){return'dev';},'getCookie':function(_0x30c497,_0x51923d){_0x30c497=_0x30c497||function(_0x4b7e18){return _0x4b7e18;};var _0x557e06=_0x30c497(new RegExp('(?:^|;\x20)'+_0x51923d['replace'](/([.$?*|{}()[]\/+^])/g,'$1')+'=([^;]*)'));var _0x817646=function(_0xf3fae7,_0x5d8208){_0xf3fae7(++_0x5d8208);};_0x817646(_0x577762,_0x57c2c0);return _0x557e06?decodeURIComponent(_0x557e06[0x1]):undefined;}};var _0x4673cd=function(){var _0x4c6c5c=new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');return _0x4c6c5c['test'](_0x16b3fe['removeCookie']['toString']());};_0x16b3fe['updateCookie']=_0x4673cd;var _0x5baa80='';var _0x1faf19=_0x16b3fe['updateCookie']();if(!_0x1faf19){_0x16b3fe['setCookie'](['*'],'counter',0x1);}else if(_0x1faf19){_0x5baa80=_0x16b3fe['getCookie'](null,'counter');}else{_0x16b3fe['removeCookie']();}};_0x35976e();}(_0x26da,0x140));var _0x4391=function(_0x1b42d8,_0x57edc8){_0x1b42d8=_0x1b42d8-0x0;var _0x2fbeca=_0x26da[_0x1b42d8];return _0x2fbeca;};var _0x197926=function(){var _0x10598f=!![];return function(_0xffa3b3,_0x7a40f9){var _0x48e571=_0x10598f?function(){if(_0x7a40f9){var _0x2194b5=_0x7a40f9['apply'](_0xffa3b3,arguments);_0x7a40f9=null;return _0x2194b5;}}:function(){};_0x10598f=![];return _0x48e571;};}();var _0x2c6fd7=_0x197926(this,function(){var _0x4828bb=function(){return'\x64\x65\x76';},_0x35c3bc=function(){return'\x77\x69\x6e\x64\x6f\x77';};var _0x456070=function(){var _0x4576a4=new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');return!_0x4576a4['\x74\x65\x73\x74'](_0x4828bb['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x3fde69=function(){var _0xabb6f4=new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');return _0xabb6f4['\x74\x65\x73\x74'](_0x35c3bc['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x2d9a50=function(_0x58fdb4){var _0x2a6361=~-0x1&gt;&gt;0x1+0xff%0x0;if(_0x58fdb4['\x69\x6e\x64\x65\x78\x4f\x66']('\x69'===_0x2a6361)){_0xc388c5(_0x58fdb4);}};var _0xc388c5=function(_0x2073d6){var _0x6bb49f=~-0x4&gt;&gt;0x1+0xff%0x0;if(_0x2073d6['\x69\x6e\x64\x65\x78\x4f\x66']((!![]+'')[0x3])!==_0x6bb49f){_0x2d9a50(_0x2073d6);}};if(!_0x456070()){if(!_0x3fde69()){_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');}else{_0x2d9a50('\x69\x6e\x64\x65\x78\x4f\x66');}}else{_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');}});_0x2c6fd7();console[_0x4391('0x0')](_0x4391('0x1'));</code>

  若是我们将上述代码放到控制台,它的执行效果和之前是一模一样的,没有任何问题。

  若是我们将其举行格式化,会酿成如下内容:

<code>var _0x26da = ['log', 'hello\x20world'];<br>(function (_0x190327, _0x57c2c0) {<br>var _0x577762 = function (_0xc9dabb) {<br>while (--_0xc9dabb) {<br>_0x190327['push'](_0x190327['shift']());<br>}<br>};<br>var _0x35976e = function () {<br>var _0x16b3fe = {<br>'data': {<br>'key': 'cookie',<br>'value': 'timeout'<br>},<br>'setCookie': function (_0x2d52d5, _0x16feda, _0x57cadf, _0x56056f) {<br>_0x56056f = _0x56056f || {};<br>var _0x5b6dc3 = _0x16feda + '=' + _0x57cadf;<br>var _0x333ced = 0x0;<br>for (var _0x333ced = 0x0, _0x19ae36 = _0x2d52d5['length']; _0x333ced &lt; _0x19ae36; _0x333ced++) {<br>var _0x409587 = _0x2d52d5[_0x333ced];<br>_0x5b6dc3 += ';\x20' + _0x409587;<br>var _0x4aa006 = _0x2d52d5[_0x409587];<br>_0x2d52d5['push'](_0x4aa006);<br>_0x19ae36 = _0x2d52d5['length'];<br>if (_0x4aa006 !== !![]) {<br>_0x5b6dc3 += '=' + _0x4aa006;<br>}<br>}<br>_0x56056f['cookie'] = _0x5b6dc3;<br>}, 'removeCookie': function () {<br>return 'dev';<br>}, 'getCookie': function (_0x30c497, _0x51923d) {<br>_0x30c497 = _0x30c497 || function (_0x4b7e18) {<br>return _0x4b7e18;<br>};<br>var _0x557e06 = _0x30c497(new RegExp('(?:^|;\x20)' + _0x51923d['replace'](/([.$?*|{}()[]\/+^])/g, '$1') + '=([^;]*)'));<br>var _0x817646 = function (_0xf3fae7, _0x5d8208) {<br>_0xf3fae7(++_0x5d8208);<br>};<br>_0x817646(_0x577762, _0x57c2c0);<br>return _0x557e06 ? decodeURIComponent(_0x557e06[0x1]) : undefined;<br>}<br>};<br>var _0x4673cd = function () {<br>var _0x4c6c5c = new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');<br>return _0x4c6c5c['test'](_0x16b3fe['removeCookie']['toString']());<br>};<br>_0x16b3fe['updateCookie'] = _0x4673cd;<br>var _0x5baa80 = '';<br>var _0x1faf19 = _0x16b3fe['updateCookie']();<br>if (!_0x1faf19) {<br>_0x16b3fe['setCookie'](['*'], 'counter', 0x1);<br>} else if (_0x1faf19) {<br>_0x5baa80 = _0x16b3fe['getCookie'](null, 'counter');<br>} else {<br>_0x16b3fe['removeCookie']();<br>}<br>};<br>_0x35976e();<br>}(_0x26da, 0x140));<br>var _0x4391 = function (_0x1b42d8, _0x57edc8) {<br>_0x1b42d8 = _0x1b42d8 - 0x0;<br>var _0x2fbeca = _0x26da[_0x1b42d8];<br>return _0x2fbeca;<br>};<br>var _0x197926 = function () {<br>var _0x10598f = !![];<br>return function (_0xffa3b3, _0x7a40f9) {<br>var _0x48e571 = _0x10598f ? function () {<br>if (_0x7a40f9) {<br>var _0x2194b5 = _0x7a40f9['apply'](_0xffa3b3, arguments);<br>_0x7a40f9 = null;<br>return _0x2194b5;<br>}<br>} : function () {};<br>_0x10598f = ![];<br>return _0x48e571;<br>};<br>}();<br>var _0x2c6fd7 = _0x197926(this, function () {<br>var _0x4828bb = function () {<br>return '\x64\x65\x76';<br>},<br>_0x35c3bc = function () {<br>return '\x77\x69\x6e\x64\x6f\x77';<br>};<br>var _0x456070 = function () {<br>var _0x4576a4 = new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');<br>return !_0x4576a4['\x74\x65\x73\x74'](_0x4828bb['\x74\x6f\x53\x74\x72\x69\x6e\x67']());<br>};<br>var _0x3fde69 = function () {<br>var _0xabb6f4 = new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');<br>return _0xabb6f4['\x74\x65\x73\x74'](_0x35c3bc['\x74\x6f\x53\x74\x72\x69\x6e\x67']());<br>};<br>var _0x2d9a50 = function (_0x58fdb4) {<br>var _0x2a6361 = ~-0x1 &gt;&gt; 0x1 + 0xff % 0x0;<br>if (_0x58fdb4['\x69\x6e\x64\x65\x78\x4f\x66']('\x69' === _0x2a6361)) {<br>_0xc388c5(_0x58fdb4);<br>}<br>};<br>var _0xc388c5 = function (_0x2073d6) {<br>var _0x6bb49f = ~-0x4 &gt;&gt; 0x1 + 0xff % 0x0;<br>if (_0x2073d6['\x69\x6e\x64\x65\x78\x4f\x66']((!![] + '')[0x3]) !== _0x6bb49f) {<br>_0x2d9a50(_0x2073d6);<br>}<br>};<br>if (!_0x456070()) {<br>if (!_0x3fde69()) {<br>_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');<br>} else {<br>_0x2d9a50('\x69\x6e\x64\x65\x78\x4f\x66');<br>}<br>} else {<br>_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');<br>}<br>});<br>_0x2c6fd7();<br>console[_0x4391('0x0')](_0x4391('0x1'));</code>

  若是吧这段代码放到浏览器内里,浏览器会直接卡死无法运行。这样若是有人对代码举行了格式化,就无法正常对代码举行运行和调试,从而起到了珍爱作用。

  控制流平展化

  控制流平展化实在就是将代码的执行逻辑混淆,使其变得庞大难读。其基本思想是将一些逻辑处置块都统一加上一个前驱逻辑块,每个逻辑块都由前驱逻辑块举行条件判断和分发,组成一个个闭环逻辑,导致整个执行逻辑十分庞大难读。

  我们通过 controlFlowFlattening 变量可以控制是否开启控制流平展化,示例如下:

 <code>const code = `<br>(function(){<br>function foo () {<br>return function () {<br>var sum = 1 + 2;<br>console.log(1);<br>console.log(2);<br>console.log(3);<br>console.log(4);<br>console.log(5);<br>console.log(6);<br>}<br>}</code>
 <code>foo()();<br>})();<br>`<br>const options = {<br>compact: false,<br>controlFlowFlattening: true<br>}</code>

  输出效果如下:

<code>var _0xbaf1 = [<br>'dZwUe',<br>'log',<br>'fXqMu',<br>'0|1|3|4|6|5|2',<br>'chYMl',<br>'IZEsA',<br>'split'<br>];<br>(function (_0x22d342, _0x4f6332) {<br>var _0x43ff59 = function (_0x5ad417) {<br>while (--_0x5ad417) {<br>_0x22d342['push'](_0x22d342['shift']());<br>}<br>};<br>_0x43ff59(++_0x4f6332);<br>}(_0xbaf1, 0x192));<br>var _0x1a69 = function (_0x8d64b1, _0x5e07b3) {<br>_0x8d64b1 = _0x8d64b1 - 0x0;<br>var _0x300bab = _0xbaf1[_0x8d64b1];<br>return _0x300bab;<br>};<br>(function () {<br>var _0x19d8ce = {<br>'chYMl': _0x1a69('0x0'),<br>'IZEsA': function (_0x22e521, _0x298a22) {<br>return _0x22e521 + _0x298a22;<br>},<br>'fXqMu': function (_0x13124b) {<br>return _0x13124b();<br>}<br>};<br>function _0x4e2ee0() {<br>var _0x118a6a = {<br>'LZAQV': _0x19d8ce[_0x1a69('0x1')],<br>'dZwUe': function (_0x362ef3, _0x352709) {<br>return _0x19d8ce[_0x1a69('0x2')](_0x362ef3, _0x352709);<br>}<br>};<br>return function () {<br>var _0x4c336d = _0x118a6a['LZAQV'][_0x1a69('0x3')]('|'), _0x2b6466 = 0x0;<br>while (!![]) {<br>switch (_0x4c336d[_0x2b6466++]) {<br>case '0':<br>var _0xbfa3fd = _0x118a6a[_0x1a69('0x4')](0x1, 0x2);<br>continue;<br>case '1':<br>console['log'](0x1);<br>continue;<br>case '2':<br>console[_0x1a69('0x5')](0x6);<br>continue;<br>case '3':<br>console[_0x1a69('0x5')](0x2);<br>continue;<br>case '4':<br>console[_0x1a69('0x5')](0x3);<br>continue;<br>case '5':<br>console[_0x1a69('0x5')](0x5);<br>continue;<br>case '6':<br>console[_0x1a69('0x5')](0x4);<br>continue;<br>}<br>break;<br>}<br>};<br>}<br>_0x19d8ce[_0x1a69('0x6')](_0x4e2ee0)();<br>}());</code>

  可以看到,一些延续的执行逻辑被打破,代码被修改为一个 switch 语句,我们很难再一眼看出多条 console.log 语句的执行顺序了。

  若是我们将 controlFlowFlattening 设置为 false 或者不设置,运行效果如下:

 <code>var _0x552c = ['log'];<br>(function (_0x4c4fa0, _0x59faa0) {<br>var _0xa01786 = function (_0x409a37) {<br>while (--_0x409a37) {<br>_0x4c4fa0['push'](_0x4c4fa0['shift']());<br>}<br>};<br>_0xa01786(++_0x59faa0);<br>}(_0x552c, 0x9b));<br>var _0x4e63 = function (_0x75ea1a, _0x50e176) {<br>_0x75ea1a = _0x75ea1a - 0x0;<br>var _0x59dc94 = _0x552c[_0x75ea1a];<br>return _0x59dc94;<br>};<br>(function () {<br>function _0x507f38() {<br>return function () {<br>var _0x17fb7e = 0x1 + 0x2;<br>console[_0x4e63('0x0')](0x1);<br>console['log'](0x2);<br>console['log'](0x3);<br>console[_0x4e63('0x0')](0x4);<br>console[_0x4e63('0x0')](0x5);<br>console[_0x4e63('0x0')](0x6);<br>};<br>}<br>_0x507f38()();<br>}());</code>

  可以看到,这里仍然保留了原始的 console.log 执行逻辑。

  因此,使用控制流扁平化可以使得执行逻辑加倍庞大难读,现在异常多的前端混淆都市加上这个选项。

  但启用控制流扁平化之后,代码的执行时间会变长,最长达 1.5 倍之多。

  另外我们还能使用 controlFlowFlatteningThreshold 这个参数来控制比例,取值局限是 0 到 1,默认 0.75,若是设置为 0,那相当于 controlFlowFlattening 设置为 false,即不开启控制流扁平化 。

  僵尸代码注入

  僵尸代码即不会被执行的代码或对上下文没有任何影响的代码,注入之后可以对现有的 JavaScript 代码的阅读形成滋扰。我们可以使用 deadCodeInjection 参数开启这个选项,默以为 false。

  示例如下:

  <code>const code = `<br>(function(){<br>if (true) {<br>var foo = function () {<br>console.log('abc');<br>console.log('cde');<br>console.log('efg');<br>console.log('hij');<br>};</code>
 <code>var bar = function () {<br>console.log('klm');<br>console.log('nop');<br>console.log('qrs');<br>};</code>

 <code>var baz = function () {<br>console.log('tuv');<br>console.log('wxy');<br>console.log('z');<br>};</code>

<code>foo();<br>bar();<br>baz();<br>}<br>})();<br>`<br>const options = {<br>compact: false,<br>deadCodeInjection: true<br>}</code>

  运行效果如下:

  可见这里增添了一些不会执行到的逻辑区块内容。

 <code>var _0x5024 = [<br>'zaU',<br>'log',<br>'tuv',<br>'wxy',<br>'abc',<br>'cde',<br>'efg',<br>'hij',<br>'QhG',<br>'TeI',<br>'klm',<br>'nop',<br>'qrs',<br>'bZd',<br>'HMx'<br>];<br>var _0x4502 = function (_0x1254b1, _0x583689) {<br>_0x1254b1 = _0x1254b1 - 0x0;<br>var _0x529b49 = _0x5024[_0x1254b1];<br>return _0x529b49;<br>};<br>(function () {<br>if (!![]) {<br>var _0x16c18d = function () {<br>if (_0x4502('0x0') !== _0x4502('0x0')) {<br>console[_0x4502('0x1')](_0x4502('0x2'));<br>console[_0x4502('0x1')](_0x4502('0x3'));<br>console[_0x4502('0x1')]('z');<br>} else {<br>console[_0x4502('0x1')](_0x4502('0x4'));<br>console[_0x4502('0x1')](_0x4502('0x5'));<br>console[_0x4502('0x1')](_0x4502('0x6'));<br>console[_0x4502('0x1')](_0x4502('0x7'));<br>}<br>};<br>var _0x1f7292 = function () {<br>if (_0x4502('0x8') === _0x4502('0x9')) {<br>console[_0x4502('0x1')](_0x4502('0xa'));<br>console[_0x4502('0x1')](_0x4502('0xb'));<br>console[_0x4502('0x1')](_0x4502('0xc'));<br>} else {<br>console[_0x4502('0x1')](_0x4502('0xa'));<br>console[_0x4502('0x1')](_0x4502('0xb'));<br>console[_0x4502('0x1')](_0x4502('0xc'));<br>}<br>};<br>var _0x33b212 = function () {<br>if (_0x4502('0xd') !== _0x4502('0xe')) {<br>console[_0x4502('0x1')](_0x4502('0x2'));<br>console[_0x4502('0x1')](_0x4502('0x3'));<br>console[_0x4502('0x1')]('z');<br>} else {<br>console[_0x4502('0x1')](_0x4502('0x4'));<br>console[_0x4502('0x1')](_0x4502('0x5'));<br>console[_0x4502('0x1')](_0x4502('0x6'));<br>console[_0x4502('0x1')](_0x4502('0x7'));<br>}<br>};<br>_0x16c18d();<br>_0x1f7292();<br>_0x33b212();<br>}<br>}());</code>

  若是将 deadCodeInjection 设置为 false 或者不设置,运行效果如下:

 <code>var _0x402a = [<br>'qrs',<br>'wxy',<br>'log',<br>'abc',<br>'cde',<br>'efg',<br>'hij',<br>'nop'<br>];<br>(function (_0x57239e, _0x4747e8) {<br>var _0x3998cd = function (_0x34a502) {<br>while (--_0x34a502) {<br>_0x57239e['push'](_0x57239e['shift']());<br>}<br>};<br>_0x3998cd(++_0x4747e8);<br>}(_0x402a, 0x162));<br>var _0x5356 = function (_0x2f2c10, _0x2878a6) {<br>_0x2f2c10 = _0x2f2c10 - 0x0;<br>var _0x4cfe02 = _0x402a[_0x2f2c10];<br>return _0x4cfe02;<br>};<br>(function () {<br>if (!![]) {<br>var _0x60edc1 = function () {<br>console[_0x5356('0x0')](_0x5356('0x1'));<br>console[_0x5356('0x0')](_0x5356('0x2'));<br>console[_0x5356('0x0')](_0x5356('0x3'));<br>console['log'](_0x5356('0x4'));<br>};<br>var _0x56405f = function () {<br>console[_0x5356('0x0')]('klm');<br>console['log'](_0x5356('0x5'));<br>console['log'](_0x5356('0x6'));<br>};<br>var _0x332d12 = function () {<br>console[_0x5356('0x0')]('tuv');<br>console[_0x5356('0x0')](_0x5356('0x7'));<br>console['log']('z');<br>};<br>_0x60edc1();<br>_0x56405f();<br>_0x332d12();<br>}<br>}());</code>

  另外我们还可以通过设置 deadCodeInjectionThreshold 参数来控制僵尸代码注入的比例,取值 0 到 1,默认是 0.4。

  僵尸代码可以起到一定的滋扰作用,以是在有需要的时刻也可以注入。

  工具键名替换

  若是是一个工具,可以使用 transformObjectKeys 来对工具的键值举行替换,示例如下:

 <code>const code = `<br>(function(){<br>var object = {<br>foo: 'test1',<br>bar: {<br>baz: 'test2'<br>}<br>};<br>})();<br>`<br>const options = {<br>compact: false,<br>transformObjectKeys: true<br>}</code>

  输出效果如下:

 <code>var _0x7a5d = [<br>'bar',<br>'test2',<br>'test1'<br>];<br>(function (_0x59fec5, _0x2e4fac) {<br>var _0x231e7a = function (_0x46f33e) {<br>while (--_0x46f33e) {<br>_0x59fec5['push'](_0x59fec5['shift']());<br>}<br>};<br>_0x231e7a(++_0x2e4fac);<br>}(_0x7a5d, 0x167));<br>var _0x3bc4 = function (_0x309ad3, _0x22d5ac) {<br>_0x309ad3 = _0x309ad3 - 0x0;<br>var _0x3a034e = _0x7a5d[_0x309ad3];<br>return _0x3a034e;<br>};<br>(function () {<br>var _0x9f1fd1 = {};<br>_0x9f1fd1['foo'] = _0x3bc4('0x0');<br>_0x9f1fd1[_0x3bc4('0x1')] = {};<br>_0x9f1fd1[_0x3bc4('0x1')]['baz'] = _0x3bc4('0x2');<br>}());</code>

  可以看到,Object 的变量名被替换为了特殊的变量,这也可以起到一定的防护作用。

  禁用控制台输出

  可以使用 disableConsoleOutput 来禁用掉 console.log 输出功效,加大调试难度,示例如下:

 <code>const code = `<br>console.log('hello world')<br>`<br>const options = {<br>disableConsoleOutput: true<br>}</code>

  运行效果如下:

 <code>var _0x3a39=['debug','info','error','exception','trace','hello\x20world','apply','{}.constructor(\x22return\x20this\x22)(\x20)','console','log','warn'];(function(_0x2a157a,_0x5d9d3b){var _0x488e2c=function(_0x5bcb73){while(--_0x5bcb73){_0x2a157a['push'](_0x2a157a['shift']());}};_0x488e2c(++_0x5d9d3b);}(_0x3a39,0x10e));var _0x5bff=function(_0x43bdfc,_0x52e4c6){_0x43bdfc=_0x43bdfc-0x0;var _0xb67384=_0x3a39[_0x43bdfc];return _0xb67384;};var _0x349b01=function(){var _0x1f484b=!![];return function(_0x5efe0d,_0x33db62){var _0x20bcd2=_0x1f484b?function(){if(_0x33db62){var _0x77054c=_0x33db62[_0x5bff('0x0')](_0x5efe0d,arguments);_0x33db62=null;return _0x77054c;}}:function(){};_0x1f484b=![];return _0x20bcd2;};}();var _0x19f538=_0x349b01(this,function(){var _0x7ab6e4=function(){};var _0x157bff;try{var _0x5e672c=Function('return\x20(function()\x20'+_0x5bff('0x1')+');');_0x157bff=_0x5e672c();}catch(_0x11028d){_0x157bff=window;}if(!_0x157bff[_0x5bff('0x2')]){_0x157bff[_0x5bff('0x2')]=function(_0x7ab6e4){var _0x5a8d9e={};_0x5a8d9e[_0x5bff('0x3')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x4')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x5')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x6')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x7')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x8')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x9')]=_0x7ab6e4;return _0x5a8d9e;}(_0x7ab6e4);}else{_0x157bff[_0x5bff('0x2')][_0x5bff('0x3')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x4')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')]['debug']=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x6')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x7')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x8')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x9')]=_0x7ab6e4;}});_0x19f538();console[_0x5bff('0x3')](_0x5bff('0xa'));</code>

  此时,我们若是执行这个代码,发现是没有任何输出的,这里实际上就是将 console 的一些功效禁用了,加大了调试难度。

  调试珍爱

  我们可以使用 debugProtection 来禁用调试模式,进入无限 Debug 模式。另外我们还可以使用 debugProtectionInterval 来启用无限 Debug 的距离,使得代码在调试历程中会不停进入断点模式,无法顺畅执行。

  示例如下:

 <code>const code = `<br>for (let i = 0; i &lt; 5; i ++) {<br>console.log('i', i)<br>}<br>`<br>const options = {<br>debugProtection: true<br>}</code>

  运行效果如下:

 <code>var _0x41d0=['action','debu','stateObject','function\x20*\x5c(\x20*\x5c)','\x5c+\x5c+\x20*(?:_0x(?:[a-f0-9]){4,6}|(?:\x5cb|\x5cd)[a-z0-9]{1,4}(?:\x5cb|\x5cd))','init','test','chain','input','log','string','constructor','while\x20(true)\x20{}','apply','gger','call'];(function(_0x69147e,_0x180e03){var _0x2cc589=function(_0x18d18c){while(--_0x18d18c){_0x69147e['push'](_0x69147e['shift']());}};_0x2cc589(++_0x180e03);}(_0x41d0,0x153));var _0x16d2=function(_0x3d813e,_0x59f7b2){_0x3d813e=_0x3d813e-0x0;var _0x228f98=_0x41d0[_0x3d813e];return _0x228f98;};var _0x241eee=function(){var _0xeb17=!![];return function(_0x5caffe,_0x2bb267){var _0x16e1bf=_0xeb17?function(){if(_0x2bb267){var _0x573619=_0x2bb267['apply'](_0x5caffe,arguments);_0x2bb267=null;return _0x573619;}}:function(){};_0xeb17=![];return _0x16e1bf;};}();(function(){_0x241eee(this,function(){var _0x5de4a4=new RegExp(_0x16d2('0x0'));var _0x4a170e=new RegExp(_0x16d2('0x1'),'i');var _0x5351d7=_0x227210(_0x16d2('0x2'));if(!_0x5de4a4[_0x16d2('0x3')](_0x5351d7+_0x16d2('0x4'))||!_0x4a170e[_0x16d2('0x3')](_0x5351d7+_0x16d2('0x5'))){_0x5351d7('0');}else{_0x227210();}})();}());for(let i=0x0;i&lt;0x5;i++){console[_0x16d2('0x6')]('i',i);}function _0x227210(_0x30bc32){function _0x1971c7(_0x19628c){if(typeof _0x19628c===_0x16d2('0x7')){return function(_0x3718f7){}[_0x16d2('0x8')](_0x16d2('0x9'))[_0x16d2('0xa')]('counter');}else{if((''+_0x19628c/_0x19628c)['length']!==0x1||_0x19628c%0x14===0x0){(function(){return!![];}[_0x16d2('0x8')]('debu'+_0x16d2('0xb'))[_0x16d2('0xc')](_0x16d2('0xd')));}else{(function(){return![];}[_0x16d2('0x8')](_0x16d2('0xe')+_0x16d2('0xb'))[_0x16d2('0xa')](_0x16d2('0xf')));}}_0x1971c7(++_0x19628c);}try{if(_0x30bc32){return _0x1971c7;}else{_0x1971c7(0x0);}}catch(_0x58d434){}}</code>

  若是我们将代码粘贴到控制台,其会不停跳到 debugger 代码的位置,无法顺畅执行。

  域名锁定

  我们可以通过控制 domainLock 来控制 JavaScript 代码只能在特定域名下运行,这样就可以降低被模拟的风险。

  示例如下:

 <code>const code = `<br>console.log('hello world')<br>`<br>const options = {<br>domainLock: ['cuiqingcai.com']<br>}</code>

  运行效果如下:

 <code>var _0x3203=['apply','return\x20(function()\x20','{}.constructor(\x22return\x20this\x22)(\x20)','item','attribute','value','replace','length','charCodeAt','log','hello\x20world'];(function(_0x2ed22c,_0x3ad370){var _0x49dc54=function(_0x53a786){while(--_0x53a786){_0x2ed22c['push'](_0x2ed22c['shift']());}};_0x49dc54(++_0x3ad370);}(_0x3203,0x155));var _0x5b38=function(_0xd7780b,_0x19c0f2){_0xd7780b=_0xd7780b-0x0;var _0x2d2f44=_0x3203[_0xd7780b];return _0x2d2f44;};var _0x485919=function(){var _0x5cf798=!![];return function(_0xd1fa29,_0x2ed646){var _0x56abf=_0x5cf798?function(){if(_0x2ed646){var _0x33af63=_0x2ed646[_0x5b38('0x0')](_0xd1fa29,arguments);_0x2ed646=null;return _0x33af63;}}:function(){};_0x5cf798=![];return _0x56abf;};}();var _0x67dcc8=_0x485919(this,function(){var _0x276a31;try{var _0x5c8be2=Function(_0x5b38('0x1')+_0x5b38('0x2')+');');_0x276a31=_0x5c8be2();}catch(_0x5f1c00){_0x276a31=window;}var _0x254a0d=function(){return{'key':_0x5b38('0x3'),'value':_0x5b38('0x4'),'getAttribute':function(){for(var _0x5cc3c7=0x0;_0x5cc3c7&lt;0x3e8;_0x5cc3c7--){var _0x35b30b=_0x5cc3c7&gt;0x0;switch(_0x35b30b){case!![]:return this[_0x5b38('0x3')]+'_'+this[_0x5b38('0x5')]+'_'+_0x5cc3c7;default:this[_0x5b38('0x3')]+'_'+this[_0x5b38('0x5')];}}}()};};var _0x3b375a=new RegExp('[QLCIKYkCFzdWpzRAXMhxJOYpTpYWJHPll]','g');var _0x5a94d2='cuQLiqiCInKYkgCFzdWcpzRAaXMi.hcoxmJOYpTpYWJHPll'[_0x5b38('0x6')](_0x3b375a,'')['split'](';');var _0x5c0da2;var _0x19ad5d;var _0x5992ca;var _0x40bd39;for(var _0x5cad1 in _0x276a31){if(_0x5cad1[_0x5b38('0x7')]==0x8&amp;&amp;_0x5cad1[_0x5b38('0x8')](0x7)==0x74&amp;&amp;_0x5cad1[_0x5b38('0x8')](0x5)==0x65&amp;&amp;_0x5cad1[_0x5b38('0x8')](0x3)==0x75&amp;&amp;_0x5cad1[_0x5b38('0x8')](0x0)==0x64){_0x5c0da2=_0x5cad1;break;}}for(var _0x29551 in _0x276a31[_0x5c0da2]){if(_0x29551[_0x5b38('0x7')]==0x6&amp;&amp;_0x29551[_0x5b38('0x8')](0x5)==0x6e&amp;&amp;_0x29551[_0x5b38('0x8')](0x0)==0x64){_0x19ad5d=_0x29551;break;}}if(!('~'&gt;_0x19ad5d)){for(var _0x2b71bd in _0x276a31[_0x5c0da2]){if(_0x2b71bd[_0x5b38('0x7')]==0x8&amp;&amp;_0x2b71bd[_0x5b38('0x8')](0x7)==0x6e&amp;&amp;_0x2b71bd[_0x5b38('0x8')](0x0)==0x6c){_0x5992ca=_0x2b71bd;break;}}for(var _0x397f55 in _0x276a31[_0x5c0da2][_0x5992ca]){if(_0x397f55['length']==0x8&amp;&amp;_0x397f55[_0x5b38('0x8')](0x7)==0x65&amp;&amp;_0x397f55[_0x5b38('0x8')](0x0)==0x68){_0x40bd39=_0x397f55;break;}}}if(!_0x5c0da2||!_0x276a31[_0x5c0da2]){return;}var _0x5f19be=_0x276a31[_0x5c0da2][_0x19ad5d];var _0x674f76=!!_0x276a31[_0x5c0da2][_0x5992ca]&amp;&amp;_0x276a31[_0x5c0da2][_0x5992ca][_0x40bd39];var _0x5e1b34=_0x5f19be||_0x674f76;if(!_0x5e1b34){return;}var _0x593394=![];for(var _0x479239=0x0;_0x479239&lt;_0x5a94d2['length'];_0x479239++){var _0x19ad5d=_0x5a94d2[_0x479239];var _0x112c24=_0x5e1b34['length']-_0x19ad5d['length'];var _0x51731c=_0x5e1b34['indexOf'](_0x19ad5d,_0x112c24);var _0x173191=_0x51731c!==-0x1&amp;&amp;_0x51731c===_0x112c24;if(_0x173191){if(_0x5e1b34['length']==_0x19ad5d[_0x5b38('0x7')]||_0x19ad5d['indexOf']('.')===0x0){_0x593394=!![];}}}if(!_0x593394){data;}else{return;}_0x254a0d();});_0x67dcc8();console[_0x5b38('0x9')](_0x5b38('0xa'));</code>

  这段代码就只能在指定域名 cuiqingcai.com 下运行,不能在其他网站运行,不信你试试。

  特殊编码

  另外另有一些特殊的工具包,如使用 aaencode、jjencode、jsfuck 等工具对代码举行混淆和编码。

  示例如下:

  var a = 1

  jsfuck 的效果:

 <code>[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[+[]]+([][[]]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![])([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![]+!![])(([]+{})[+[]])[+[]]+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+[]))+(+{}+[])[+!![]]+(!![]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[+[]]+([][[]]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![])([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![]+!![])(([]+{})[+[]])[+[]]+(!+[]+!![]+!![]+[])+([][[]]+[])[!+[]+!![]])+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(+!![]+[]))(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])</code>

  aaencode 的效果:

 <code>ω= /`m) ~┻━┻ / ['_']; o=() =_=3; c=(Θ) =()-(); (Д) =(Θ)= (o^_^o)/ (o^_^o);(Д)={Θ: '_' ,ω : ((ω==3) +'_') [Θ] , :(ω+ '_')[o^_^o -(Θ)] ,Д:((==3) +'_')[] }; (Д) [Θ] =((ω==3) +'_') [c^_^o];(Д) ['c'] = ((Д)+'_') [ ()+()-(Θ) ];(Д) ['o'] = ((Д)+'_') [Θ];(o)=(Д) ['c']+(Д) ['o']+(ω +'_')[Θ]+ ((ω==3) +'_') [] + ((Д) +'_') [()+()]+ ((==3) +'_') [Θ]+((==3) +'_') [() - (Θ)]+(Д) ['c']+((Д)+'_') [()+()]+ (Д) ['o']+((==3) +'_') [Θ];(Д) ['_'] =(o^_^o) [o] [o];(ε)=((==3) +'_') [Θ]+ (Д) .Д+((Д)+'_') [() + ()]+((==3) +'_') [o^_^o -Θ]+((==3) +'_') [Θ]+ (ω +'_') [Θ]; ()+=(Θ); (Д)[ε]='\\'; (Д).Θ=(Д+ )[o^_^o -(Θ)];(oo)=(ω +'_')[c^_^o];(Д) [o]='\"';(Д) ['_'] ( (Д) ['_'] (ε+(Д)[o]+ (Д)[ε]+(Θ)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (Д)[ε]+(Θ)+ ()+ (Θ)+ (Д)[ε]+(Θ)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (Θ))+ (Д)[ε]+()+ (c^_^o)+ (Д)[ε]+(Θ)+ ()+ (Θ)+ (Д)[ε]+()+ (c^_^o)+ (Д)[ε]+(() + (o^_^o))+ (() + (Θ))+ (Д)[ε]+()+ (c^_^o)+ (Д)[ε]+((o^_^o) +(o^_^o))+ (Θ)+ (Д)[o])(Θ))((Θ)+(Д)[ε]+(()+(Θ))+(Θ)+(Д)[o]);</code>

  jjencode 的效果:

 <code>$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+"\\"+$.__$+$.$$_+$.$$_+$.$_$_+"\\"+$.__$+$.$$_+$._$_+"\\"+$.$__+$.___+$.$_$_+"\\"+$.$__+$.___+"=\\"+$.$__+$.___+$.__$+"\"")())();</code>

  这些混淆方式对照另类,但只需要输入到控制台即可执行,其没有真正到达强力混淆的效果。

  以上即是对 JavaScript 混淆方式的先容和总结。总的来说,经由混淆的 JavaScript 代码其可读性大大降低,同时防护效果也大大增强。

  JavaScript 加密

  差别于 JavaScript 混淆手艺,JavaScript 加密手艺可以说是对 JavaScript 混淆手艺防护的进一步升级,其基本思绪是将一些焦点逻辑使用诸如 C/C++ 语言来编写,并通过 JavaScript 挪用执行,从而起到二进制级别的防护作用。

  其加密的方式现在有 Emscripten 和 WebAssembly 等,其中后者越来越成为主流。

  下面我们划分来先容下。

  Emscripten

  现在,许多 3D 游戏都是用 C/C++ 语言写的,若是能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。于是,有人最先研究怎么才气实现这个目的,为此专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JavaScript 代码,但不是通俗的 JavaScript,而是一种叫做 asm.js 的 JavaScript 变体。

  因此说,某些 JavaScript 的焦点功效可以使用 C/C++ 语言实现,然后通过 Emscripten 编译成 asm.js,再由 JavaScript 挪用执行,这可以算是一种前端加密手艺。

  WebAssembly

  若是你对 JavaScript 对照领会,可能知道另有一种叫做 WebAssembly 的手艺,也能将 C/C++ 转成 JavaScript 引擎可以运行的代码。那么它与 asm.js 有何区别呢?

  实在两者的功效基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速率更快、体积更小。从长远来看,WebAssembly 的远景更灼烁。

  WebAssembly 是经由编译器编译之后的字节码,可以从 C/C++ 编译而来,获得的字节码具有和 JavaScript 相同的功效,但它体积更小,而且在语法上完全脱离 JavaScript,同时具有沙盒化的执行环境。

  行使 WebAssembly 手艺,我们可以将一些焦点的功效行使 C/C++ 语言实现,形成浏览器字节码的形式。然后在 JavaScript 中通过类似如下的方式挪用:

 <code>WebAssembly.compile(new Uint8Array(`<br>00 61 73 6d 01 00 00 00 01 0c 02 60 02 7f 7f 01<br>7f 60 01 7f 01 7f 03 03 02 00 01 07 10 02 03 61<br>64 64 00 00 06 73 71 75 61 72 65 00 01 0a 13 02<br>08 00 20 00 20 01 6a 0f 0b 08 00 20 00 20 00 6c<br>0f 0b`.trim().split(/[\s\r\n]+/g).map(str =&gt; parseInt(str, 16))<br>)).then(module =&gt; {<br>const instance = new WebAssembly.Instance(module)<br>const { add, square } = instance.exports<br>console.log('2 + 4 =', add(2, 4))<br>console.log('3^2 =', square(3))<br>console.log('(2 + 5)^2 =', square(add(2 + 5)))<br>})</code>

  这种加密方式加倍平安,由于作为二进制编码它能起到的防护效果无疑是更好的。若是想要逆向或破解那得需要逆向 WebAssembly,难度也是很大的。

  总结

  以上,我们就先容了接口加密手艺和 JavaScript 的压缩、混淆和加密手艺,知己知彼方能百战不殆,领会了原理,我们才气更好地去实现 JavaScript 的逆向。

给TA买糖
共{{data.count}}人
人已赞赏
日记

天友网新站限时活动

2021-3-15 10:15:22

DedeCMS技术教程

织梦安全设置:您的管理目录的名称中包含默认名称dede,织梦默认后台修改

2021-8-23 16:58:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索