当前位置: 首页 > news >正文

网站加密和混淆技术简介

我们在爬取网站的时候,会遇到一些需要分析接口或 URL 信息的情况,这时会有各种各样类似加密的情况

1. 某个网站的URL 带有一些看不懂的长串加密字符,要抓取就必须懂的这些参数是怎么构造的,否则我们连完整的 URL 都构造不出来,更不用说爬取了

2. 在分析某个网站的 Ajax 接口时,可以看到接口的一些参数也是加密的, Request Headers 里面的也可能带有一些加密参数, 如果不知道这些参数的具体构造逻辑,就没法直接用程序来模拟这些 Ajax 请求

3. 翻看网站的 JavaScrapt 源代码,可以发现很多压缩了或者看不太懂字符, 比如 JavaScrapt 文件名被编码, 文件的内容被压缩成几行, 变量被修改成单个字符或者一些十六进制的字符,这些导致我们无法轻易根据 JavaScrapt 源代码找出某些接口的加密逻辑

以上情况基本上是网站为了保护其数据而采取的一些措施,我们可以把它分为两大类

1. URL/API 参数加密

2. JavaScrapt 压缩,混淆和加密

网站数据防护方案

URL/API 参数加密

网站运营者首先想到的防护措施可能是对某些数据接口的参数进行加密,比如给某些 URL 的参数加上校验码, 给一些 ID 信息编码, 给某些 API 请求加上 token/ sign 等签名, 这样这些请求发送到服务器时,服务器会通过客户端发来的一些请求信息以及双方约定好的秘钥等来对当前的的请求进行校验,只有校验通过,才返回对应的数据结果

JavaScript 压缩,混淆 和 加密

接口加密技术看起来确实是一个不错的方案,但是单纯依靠它并不能很好的解决问题。因为:

1. JavaScript 运行于客户端,也就是它必须在用户浏览器端加载并运行

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

基于这两个原因, JavaScript 代码是不安全的,任何人都可以读,分析,复制,盗用甚至篡改代码

所以说,对于以上情形,客户端 JavaScript 对于某些加密的实现是很容易被找到或模拟的,了解了加密逻辑后,模拟参数的构造和请求也就轻而易举了,所以如果 JavaScript 没有做任何层面的保护的话,接口的加密对数据起不到任何防护作用

如果不想数据被这么轻易获取,那么就要用到 JavaScript 压缩,混淆和加密技术了

代码压缩: 去除 JavaScript 代码中不必要的空格,换行等内容,使源代码压缩为几行内容,降低代码的可读性,当然同时也提高了网站的加载速度

代码混淆: 使用变量替换,字符串阵列化,控制流平坦化,多态变异,僵尸函数,调试保护等手段,是代码变得难以阅读和分析,达到最终保护的目的。但这并不影响代码原有的功能,使理想实用的 JavaScript 保护方案

代码加密:可以通过某种手段将 JavaScript  代码进行加密, 转成人无法阅读或者解析的代码,如借用 WebAssembly 技术,可以直接将 JavaScript 代码用 C/C++ 实现, JavaScript 调用其编译后形成的文件执行相应的功能

URL/API 参数加密

现在绝大多数的网站的数据一般都通过服务器提供的API 来获取的,网站或 App 可以请求某个数据 API 获取对应的数据,然后在把数据展示出来。 但有些数据是比较宝贵或私密的,这些数据肯定需要一些层面上的防护,所以不同的 API 的实现也就对应着不同的安全防护级别

为了提升接口的安全性,客户端和服务端约定一种接口校验方式,一般来说会用到各种加密和编码算法, 如 Base64, Hex 编码, MD5 , AES, DES, RSA 等对称或非对称加密

JavaScript 压缩

JavaScript 压缩其实就是去除 JavaScript 代码中不必要的空格,换行等内容或者把一些可能公用的代码进行处理实现共享,最后输出的结果压缩为几行内容,代码的可读性变的很差,同时仅仅去除空格,换行这样的压缩方式,其实几乎是没有任何防护作用的,因为这种压缩方式仅仅是降低了代码的直接可读性。 因为网上有一些常见的工具就可以直接将其格式化

目前主流的前端开发技术大多都会利用 webpack , Rollup 等工具进行打包。 webpack, Rollup 会对源代码进行编译和压缩,输出几个打包好的 JavaScript 文件,其中我们可以看到输出的 JavaScript 文件名带有一些不规则字符串,同时文件内容可能只有几行, 变量名都用一些简单的字母表示。这其中就包含 JavaScript 压缩技术, 比如一些公共的库输出成 bundle 文件,一些调用逻辑压缩和转义成冗长的几行代码,这些都属于 JavaScript 压缩。 

JavaScript 混淆

JavaScript 混淆完全是在 JavaScript 上面进行的处理, 它的目的就是使得 JavaScript 变得难以阅读和分析。

变量名混淆: 将带有含义的变量名,方法名,常量名随机变为毫无意义的乱码字符串

字符串混淆: 将字符串阵列化集中放置并进行 MD5 或 Base64 加密存储,使代码不出现明文字符串,这样可以避免使用全局搜索字符串的方式定位到入口

对象键名替换: 针对 JavaScript 对象的属性进行加密转化,隐藏代码之间的调用关系

控制流平坦化:  打乱函数原有的代码的执行流程及调用关系,使代码逻辑变得混乱无序

无用代码注入: 随机在代码中插入不会被执行的无用代码,进一步是代码看起更加混乱

调试保护:基于调试器的特性,对当前运行环境进行校验, 加入一些 debugger 语句,使其在调试模式下难以顺利执行 JavaScript 代码

多态变异: 使JavaScript 每次被调试调用时,将代码自身立刻发生变异,变为与之前完全不同的代码,即功能不变,只是代码形式发生变异,以杜绝代码被动态分析和调试

域名锁定:使 JavaScript 代码只能在指定域名下运行

特殊编码: 将 JavaScript 完全编码为人不可读的代码,比如表情符号,特殊符号等

在前端开发中,现在实现 JavaScript 混淆的主流实现是 javascript-obfscatr 和 terser 这两个库

以 javascript-obfuscator 为例, 它是支持 ES8 的免费,高效的 JavaScript 混淆库, 可以使得 JavaScript 代码经过混淆后难以被复用,盗用,混淆后的代码具有和原来的代码一模一样的功能

怎么使用能? 首先 要装好 Node.js 12.x 及以上的版本, 确保可以正常使用 npm 命令

首先创建一个项目:

1. 创建一个文件夹 例如: js-obfuscate 

2. 使用工具打开文件夹, 例如 vscode   cmd 都行

3. 输入 npm init  (前面装了 Node.js ,并可以使用 npm 命令)

4. 会出现一系列的提示

package name: 你的项目名字叫啥
version: 版本号 (默认 1.0.0)(可选)
description: 对项目的描述(可选)
entry point: 项目的入口文件(默认 index.js)(可选)
test command: 项目启动的时候要用什么命令来执行脚本文件(默认为node app.js)(可选)
git repository: 如果你要将项目上传到git中的话,那么就需要填写git的仓库地址(可选)
keywirds: 项目关键字(我也不知道有啥用,所以我就不写了)
author: 作者的名字(可选)
license: 发行项目需要的证书(可选)

完成之后会出现一个 package.json

然后安装 js-obfuscate 库

npm i -D javascript-obfuscator

安装的时候如果报错,有可能是网络问题,可以多试几次,如果出现了 node_modules 的文件夹依然会报错,可以将命令再执行一遍(不要删之前安装过的),让他继续安装

安装完成之后,node_modules 文件夹下会出现很多的文件夹, 表示安装成功了

创建一个 main.js 文件

const code = `

let x = '1' + 1

console.log('x', x)

`

const options = {

  compact: false,

  controlFlowFlattening: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

在命令行  :  node main.js  运行之后

(function (_0x4d092c, _0x2f5762) {
    const _0x281041 = _0x26c1, _0x27e2de = _0x4d092c();
    while (!![]) {
        try {
            const _0x3151b1 = parseInt(_0x281041(0x1f4)) / 0x1 * (parseInt(_0x281041(0x1f3)) / 0x2) + -parseInt(_0x281041(0x1f7)) / 0x3 * (-parseInt(_0x281041(0x1f5)) / 0x4) + parseInt(_0x281041(0x1f8)) / 0x5 + parseInt(_0x281041(0x1f2)) / 0x6 + -parseInt(_0x281041(0x1fc)) / 0x7 * (-parseInt(_0x281041(0x1fb)) / 0x8) + -parseInt(_0x281041(0x1f9)) / 0x9 + parseInt(_0x281041(0x1f6)) / 0xa * (-parseInt(_0x281041(0x1fa)) / 0xb);
            if (_0x3151b1 === _0x2f5762)
                break;
            else
                _0x27e2de['push'](_0x27e2de['shift']());
        } catch (_0x456666) {
            _0x27e2de['push'](_0x27e2de['shift']());
        }
    }
}(_0x3a8e, 0x236c9));
let x = '1' + 0x1;
function _0x26c1(_0x31b95d, _0x36c78f) {
    const _0x3a8e23 = _0x3a8e();
    return _0x26c1 = function (_0x26c120, _0x2b8ed3) {
        _0x26c120 = _0x26c120 - 0x1f2;
        let _0x35ab0b = _0x3a8e23[_0x26c120];
        return _0x35ab0b;
    }, _0x26c1(_0x31b95d, _0x36c78f);
}
console['log']('x', x);
function _0x3a8e() {
    const _0x4ea833 = [
        '232AFrliB',
        '20oCyCxY',
        '13893ZUBQDQ',
        '225280tUMyyY',
        '1679427ppLFEE',
        '2203410wXvAkQ',
        '280ZIEIAh',
        '41482jszBjV',
        '502092KVtJsN',
        '127574cmdaFW',
        '2OBrzKA'
    ];
    _0x3a8e = function () {
        return _0x4ea833;
    };
    return _0x3a8e();

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

代码压缩

javascript-obfuscator 也提供了代码压缩功能, 使用其参数 compact 即可完成 JavaScript 代码的压缩,输出为一行内容。参数 compact 的默认值是 true ,如果定义为 false 那么混淆后的代码会分行展示

const code = `

let x = '1' + 1

console.log('x', x)

`

const options = {

  compact: true,

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

 可以看到,代码完全挤在一起了

const _0x5c6b41=_0x546d;(function(_0xebf384,_0x54f3a9){const _0x302b00=_0x546d,_0x367638=_0xebf384();while(!![]){try{const _0x249619=parseInt(_0x302b00(0x183))/0x1+-parseInt(_0x302b00(0x185))/0x2*(parseInt(_0x302b00(0x17f))/0x3)+parseInt(_0x302b00(0x182))/0x4+-parseInt(_0x302b00(0x188))/0x5+-parseInt(_0x302b00(0x187))/0x6*(parseInt(_0x302b00(0x186))/0x7)+parseInt(_0x302b00(0x184))/0x8*(-parseInt(_0x302b00(0x180))/0x9)+parseInt(_0x302b00(0x181))/0xa;if(_0x249619===_0x54f3a9)break;else _0x367638['push'](_0x367638['shift']());}catch(_0x9b3150){_0x367638['push'](_0x367638['shift']());}}}(_0x15fd,0xb25e6));function _0x15fd(){const _0x274d2e=['56jlGlMY','4TBRzYu','203VZEcKD','23334gBqhhL','767055ydWwNV','log','2186532ofYyfi','666351DYipZp','3326350TuZLDM','5096152PuNVOt','1366078zwxueh'];_0x15fd=function(){return _0x274d2e;};return _0x15fd();}let x='1'+0x1;function _0x546d(_0x51e0c2,_0x52160b){const _0x15fd52=_0x15fd();return _0x546d=function(_0x546d84,_0x12968b){_0x546d84=_0x546d84-0x17f;let _0x3a2bc9=_0x15fd52[_0x546d84];return _0x3a2bc9;},_0x546d(_0x51e0c2,_0x52160b);}console[_0x5c6b41(0x189)]('x',x);

变量名混淆

变量名混淆可以通过在 javascript-obfuscator 中配置 identifierNamesGenerator 参数来实现。如果将其值设为 hexadecimal ,则会将变量名替换为 十六进制形式的字符串

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

mangled : 将变量名替换为普通的简写字符 例如 a, b , c等

const code = `

let hello = '1' + 1

console.log('hello', hello)

`

const options = {

  compact: true,

  identifierNamesGenerator: 'mangled'

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

const i=b;(function(c,d){const h=b,e=c();while(!![]){try{const f=parseInt(h(0x10e))/0x1*(-parseInt(h(0x10c))/0x2)+-parseInt(h(0x111))/0x3+parseInt(h(0x115))/0x4*(parseInt(h(0x114))/0x5)+parseInt(h(0x112))/0x6*(-parseInt(h(0x10f))/0x7)+-parseInt(h(0x110))/0x8+-parseInt(h(0x113))/0x9+parseInt(h(0x117))/0xa;if(f===d)break;else e['push'](e['shift']());}catch(g){e['push'](e['shift']());}}}(a,0x6c52f));function a(){const j=['26031360NSHRFA','139570bWOiFJ','log','10vsDbgU','7WyBWmQ','25168GNiOri','1462830btnoTs','5186778KGtaFW','6696261JlGafx','3188285UAKRGV','4oDzIAv','hello'];a=function(){return j;};return a();}function b(c,d){const e=a();return b=function(f,g){f=f-0x10c;let h=e[f];return h;},b(c,d);}let hello='1'+0x1;console[i(0x10d)](i(0x116),hello);

可以看到变量名变成了 a ,b 的形式

也可以设置 identifiersPrefix 参数来控制混淆后的变量前缀

const code = `

let hello = '1' + 1

console.log('hello', hello)

`

const options = {

  identifiersPrefix: 'germey'

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

const germey_0x5a7cec=germey_0x3c79;function germey_0x1462(){const _0x256596=['5FxMIWA','14876HiEAWk','4175800QHkiZt','2604196ZCrwwo','95890cnxtCC','20DszwMR','log','30mIbOUa','1720938eHRKlL','137304kEtZRb','2506077rvjWdq'];germey_0x1462=function(){return 
_0x256596;};return germey_0x1462();}(function(_0x946c93,_0x40e486){const _0x206272=germey_0x3c79,_0x136653=_0x946c93();while(!![]){try{const _0x3771d9=parseInt(_0x206272(0x106))/0x1*(-parseInt(_0x206272(0x105))/0x2)+parseInt(_0x206272(0x10a))/0x3+parseInt(_0x206272(0x10d))/0x4*(-parseInt(_0x206272(0x10c))/0x5)+-parseInt(_0x206272(0x109))/0x6+parseInt(_0x206272(0x104))/0x7+parseInt(_0x206272(0x10e))/0x8+parseInt(_0x206272(0x10b))/0x9*(parseInt(_0x206272(0x108))/0xa);if(_0x3771d9===_0x40e486)break;else _0x136653['push'](_0x136653['shift']());}catch(_0x15bd60){_0x136653['push'](_0x136653['shift']());}}}(germey_0x1462,0x80578));let hello='1'+0x1;function germey_0x3c79(_0x581c10,_0x5dc5b2){const _0x14622a=germey_0x1462();return germey_0x3c79=function(_0x3c79bf,_0xef6b91){_0x3c79bf=_0x3c79bf-0x104;let _0x5e9c40=_0x14622a[_0x3c79bf];return _0x5e9c40;},germey_0x3c79(_0x581c10,_0x5dc5b2);}console[germey_0x5a7cec(0x107)]('hello',hello);

可以看到,混淆后的变量名前缀加上了我们自定义的 germey 

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

const code = `

var $ = function(id) {

    return document.getElementById(id);

};

`

const options = {

  renameGlobals: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

(function(_0x2126f5,_0x4f498b){var _0x448046=_0x10b3,_0x4912e1=_0x2126f5();while(!![]){try{var _0x5c646b=-parseInt(_0x448046(0x1c3))/0x1*(parseInt(_0x448046(0x1be))/0x2)+parseInt(_0x448046(0x1bf))/0x3*(-parseInt(_0x448046(0x1b9))/0x4)+parseInt(_0x448046(0x1bd))/0x5*(parseInt(_0x448046(0x1bc))/0x6)+-parseInt(_0x448046(0x1c0))/0x7+-parseInt(_0x448046(0x1c2))/0x8+parseInt(_0x448046(0x1bb))/0x9+-parseInt(_0x448046(0x1c4))/0xa*(-parseInt(_0x448046(0x1ba))/0xb);if(_0x5c646b===_0x4f498b)break;else _0x4912e1['push'](_0x4912e1['shift']());}catch(_0x5e6fb7){_0x4912e1['push'](_0x4912e1['shift']());}}}(_0x4ccb,0x6f00e));function _0x10b3(_0x219820,_0x29bda8){var _0x4ccb5c=_0x4ccb();return _0x10b3=function(_0x10b31f,_0x3eb05f){_0x10b31f=_0x10b31f-0x1b9;var _0x1d07f9=_0x4ccb5c[_0x10b31f];return _0x1d07f9;},_0x10b3(_0x219820,_0x29bda8);}var _0x2695a4=function(_0x524c7c){var _0x5dfa95=_0x10b3;return 
document[_0x5dfa95(0x1c1)](_0x524c7c);};function _0x4ccb(){var _0x58c9f1=['getElementById','347616xHAGlf','49906zAzpCm','1090EPDXUe','3556248GSaYKz','229394zZtUgU','700569Wnqqmy','6ofVjXU','1200825mUOHDu','26QTSxHw','3iUmEtj','3885910ffHvVO'];_0x4ccb=function(){return _0x58c9f1;};return _0x4ccb();}

可以看到我们定义的 $ 不见了,如果后文用到了这个变量,可能会出错,因此酌情设置

字符串混淆

字符串混淆,即将一个字符串声明放到一个数组里面, 使之无法被直接搜到。 这可以通过 stringArray 参数控制, 默认是 true 。 此外还可以通过 rotateStringArray 参数来控制数组化后结果的元素顺序, 默认为 true 。 还可以通过 stringArrayEncoding 参数来控制数组的编码形式, 默认不开启编码。 如果将其设置为 true 或 base64 ,则会使用 Base64 另外,还可以通过 stringArrayThreshold 来控制启用编码的概率, 其范围为 0-1 默认为 0.8

const code = `

var a = 'hello world'  

`

const options = {

  stringArray: true,

  rotateStringArray: true,

  stringArrayEncoding: ['base64',], // 'base64' or 'rc4' or false

  stringArrayThreshold: 1,

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

var _0x35131b=_0x4590;(function(_0x19c057,_0xc27b2d){var _0x15721b=_0x4590,_0xa11bed=_0x19c057();while(!![]){try{var _0x1e0433=parseInt(_0x15721b(0x1f0))/0x1*(-parseInt(_0x15721b(0x1ee))/0x2)+parseInt(_0x15721b(0x1f6))/0x3*(parseInt(_0x15721b(0x1f5))/0x4)+-parseInt(_0x15721b(0x1ef))/0x5+parseInt(_0x15721b(0x1f7))/0x6+-parseInt(_0x15721b(0x1f9))/0x7*(parseInt(_0x15721b(0x1f8))/0x8)+-parseInt(_0x15721b(0x1f3))/0x9*(-parseInt(_0x15721b(0x1f4))/0xa)+-parseInt(_0x15721b(0x1f1))/0xb;if(_0x1e0433===_0xc27b2d)break;else _0xa11bed['push'](_0xa11bed['shift']());}catch(_0x5865c7){_0xa11bed['push'](_0xa11bed['shift']());}}}(_0x2531,0xe366a));function _0x2531(){var _0x4fc026=['otrVC3LczuG','mJi4nZCXne1JrKDPtG','AgvSBg8GD29YBgq','owTLAunqBq','nZqXmtu5mefgtMXYuW','nezctNLXqW','mtGZndyXn0D0y1nxEa','mteWmdC3mJb6v0DvrNi','nJq4ndy0qu5ms2Hx','nZDXD2HLtKi','otC0ogTZD0zLAG','mZq5mdu4merhBgLRBW'];_0x2531=function(){return _0x4fc026;};return _0x2531();}function _0x4590(_0x1d9eee,_0x2ca6fa){var _0x2531df=_0x2531();return _0x4590=function(_0x45909f,_0x694dc4){_0x45909f=_0x45909f-0x1ee;var _0x5b178c=_0x2531df[_0x45909f];if(_0x4590['jbdtDM']===undefined){var _0x283592=function(_0x581a43){var _0x23f657='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x3f5727='',_0x1dd216='';for(var _0x5ea4f8=0x0,_0x18de19,_0x42a870,_0x5edc64=0x0;_0x42a870=_0x581a43['charAt'](_0x5edc64++);~_0x42a870&&(_0x18de19=_0x5ea4f8%0x4?_0x18de19*0x40+_0x42a870:_0x42a870,_0x5ea4f8++%0x4)?_0x3f5727+=String['fromCharCode'](0xff&_0x18de19>>(-0x2*_0x5ea4f8&0x6)):0x0){_0x42a870=_0x23f657['indexOf'](_0x42a870);}for(var _0x427e2f=0x0,_0x3a1436=_0x3f5727['length'];_0x427e2f<_0x3a1436;_0x427e2f++){_0x1dd216+='%'+('00'+_0x3f5727['charCodeAt'](_0x427e2f)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1dd216);};_0x4590['RwPTRB']=_0x283592,_0x1d9eee=arguments,_0x4590['jbdtDM']=!![];}var _0x467615=_0x2531df[0x0],_0x24ad2f=_0x45909f+_0x467615,_0x1db7f0=_0x1d9eee[_0x24ad2f];return!_0x1db7f0?(_0x5b178c=_0x4590['RwPTRB'](_0x5b178c),_0x1d9eee[_0x24ad2f]=_0x5b178c):_0x5b178c=_0x1db7f0,_0x5b178c;},_0x4590(_0x1d9eee,_0x2ca6fa);}var a=_0x35131b(0x1f2);

可以看到 字符串已经找不到了,另外我们还可以用 unicodeEscapeSequence 这个参数对字符串进行 Unicode  转码

const code = `

var a = 'hello world'

`

const options = {

  compact: false,

  unicodeEscapeSequence: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x363c(_0x228c16, _0x48aa18) {
    var _0x25c699 = _0x25c6();
    return _0x363c = function (_0x363c29, _0x5b2b9c) {
        _0x363c29 = _0x363c29 - 0xaa;
        var _0x23386b = _0x25c699[_0x363c29];
        return _0x23386b;
    }, _0x363c(_0x228c16, _0x48aa18);
}
var _0x940dfd = _0x363c;
(function (_0x19f7da, _0xf3603f) {
    var _0x4e557e = _0x363c, _0x24e454 = _0x19f7da();
    while (!![]) {
        try {
            var _0x3bd4d1 = -parseInt(_0x4e557e(0xb1)) / 0x1 * (-parseInt(_0x4e557e(0xb0)) / 0x2) + parseInt(_0x4e557e(0xac)) / 
0x3 + -parseInt(_0x4e557e(0xaf)) / 0x4 + -parseInt(_0x4e557e(0xab)) / 0x5 * (-parseInt(_0x4e557e(0xad)) / 0x6) + parseInt(_0x4e557e(0xb2)) / 0x7 + -parseInt(_0x4e557e(0xb4)) / 0x8 + -parseInt(_0x4e557e(0xb3)) / 0x9 * (parseInt(_0x4e557e(0xaa)) / 0xa);     
            if (_0x3bd4d1 === _0xf3603f)
                break;
            else
                _0x24e454['push'](_0x24e454['shift']());
        } catch (_0x5cfa3b) {
            _0x24e454['push'](_0x24e454['shift']());
        }
    }
}(_0x25c6, 0x56b69));
function _0x25c6() {
    var _0x1fbea8 = [
        '\x32\x30\x31\x32\x35\x35\x32\x55\x6c\x4d\x6a\x76\x56',
        '\x31\x30\x61\x75\x53\x66\x4d\x48',
        '\x33\x35\x30\x37\x38\x33\x30\x49\x53\x72\x64\x61\x64',
        '\x31\x33\x31\x35\x35\x30\x33\x71\x55\x66\x63\x6a\x4a',
        '\x36\x57\x59\x45\x45\x50\x49',
        '\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64',
        '\x31\x38\x30\x34\x34\x30\x56\x71\x4d\x6b\x7a\x50',
        '\x32\x36\x63\x76\x51\x74\x6a\x43',
        '\x32\x36\x34\x39\x44\x79\x61\x79\x51\x41',
        '\x34\x35\x34\x39\x30\x33\x34\x53\x45\x48\x78\x62\x63',
        '\x31\x30\x35\x35\x32\x35\x39\x30\x4c\x50\x62\x67\x78\x51'
    ];
    _0x25c6 = function () {
        return _0x1fbea8;
    };
    return _0x25c6();
}
var a = _0x940dfd(0xae);

代码的自我保护

我们可以通过设置 selfDefending 参数来开启代码的自我保护功能。 开启之后, 混淆后的 JavaScript 会强制以一行形式显示。 如果我们将混淆后的代码进行格式化或者重命名,该段代码将无法执行

const code = `

console.log('hello world')

`

const options = {

  selfDefending: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

var _0xd11e3f=_0x439f;(function(_0x1c0305,_0x3a57cf){var _0x4bda0c=_0x439f,_0x404764=_0x1c0305();while(!![]){try{var _0x3613bc=-parseInt(_0x4bda0c(0x87))/0x1*(parseInt(_0x4bda0c(0x8f))/0x2)+parseInt(_0x4bda0c(0x91))/0x3*(parseInt(_0x4bda0c(0x92))/0x4)+-parseInt(_0x4bda0c(0x8c))/0x5*(-parseInt(_0x4bda0c(0x96))/0x6)+-parseInt(_0x4bda0c(0x94))/0x7+-parseInt(_0x4bda0c(0x95))/0x8*(-parseInt(_0x4bda0c(0x93))/0x9)+parseInt(_0x4bda0c(0x8e))/0xa+-parseInt(_0x4bda0c(0x90))/0xb*(parseInt(_0x4bda0c(0x8b))/0xc);if(_0x3613bc===_0x3a57cf)break;else _0x404764['push'](_0x404764['shift']());}catch(_0x2b7399){_0x404764['push'](_0x404764['shift']());}}}(_0x4dd7,0xb197e));var _0x3a491c=(function(){var _0x1820bc=!![];return function(_0x3fdfea,_0x148e5d){var _0x56780d=_0x1820bc?function(){var _0xead5db=_0x439f;if(_0x148e5d){var _0xe980a0=_0x148e5d[_0xead5db(0x8d)](_0x3fdfea,arguments);return _0x148e5d=null,_0xe980a0;}}:function(){};return _0x1820bc=![],_0x56780d;};}()),_0x5d3317=_0x3a491c(this,function(){var _0x4d62e8=_0x439f;return _0x5d3317['toString']()[_0x4d62e8(0x88)](_0x4d62e8(0x89))['toString']()[_0x4d62e8(0x8a)](_0x5d3317)['search'](_0x4d62e8(0x89));});function _0x439f(_0x254843,_0x1192ee){var _0x28eb15=_0x4dd7();return _0x439f=function(_0x5d3317,_0x3a491c){_0x5d3317=_0x5d3317-0x87;var _0x4dd7dc=_0x28eb15[_0x5d3317];return _0x4dd7dc;},_0x439f(_0x254843,_0x1192ee);}function _0x4dd7(){var _0x36da29=['constructor','12LIMnts','3410545JdsbGk','apply','5684260usRpJN','2086ZOaUxx','16507139lursEK','1448274nlOFbv','4VBSaXz','2297367hVvMsn','4346405zFrRkF','40iaruMA','6lVbCUP','log','154hbVypx','search','(((.+)+)+)+$'];_0x4dd7=function(){return _0x36da29;};return _0x4dd7();}_0x5d3317(),console[_0xd11e3f(0x97)]('hello\x20world');

如果我们将混淆后的代码运行是可以正常使用的, 如果格式化之后再运行则会报错

控制流平坦化

控制流平坦化就是将代码的执行逻辑混淆, 使其变得复杂难读。其基本思想是将一些逻辑处理快都统一加上一个前驱逻辑块, 每个逻辑块都有前驱逻辑块进行条件判断和分发, 构成一个个闭环逻辑,这导致整个逻辑十分复杂

在 javascript-obfuscator 中我们可以使用 controlFlowFlattening 变量控制是否开启控制流平坦化

const code = `

(function(){

    function foo () {

        return function () {

            var sum = 1 + 2;

            console.log(1);

            console.log(2);

            console.log(3);

            console.log(4);

            console.log(5);

            console.log(6);

        }

    }

   

    foo()();

})();

`

const options = {

  compact: false,

  controlFlowFlattening: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x58e5(_0x27b691, _0x5104fa) {
    var _0x1e139d = _0x1e13();
    return _0x58e5 = function (_0x58e5c3, _0x23afd1) {
        _0x58e5c3 = _0x58e5c3 - 0x126;
        var _0x417bcb = _0x1e139d[_0x58e5c3];
        return _0x417bcb;
    }, _0x58e5(_0x27b691, _0x5104fa);
}
function _0x1e13() {
    var _0xe5c863 = [
        '10721970nxDJls',
        'log',
        '150712pXmEVE',
        '182CzrguC',
        '7428828IljWVM',
        '1623ImHQdj',
        '524130sdQmZL',
        '166202xBaANC',
        '5NZTnsL',
        'split',
        '16520290nSzLpD',
        '448PoSvMN'
    ];
    _0x1e13 = function () {
        return _0xe5c863;
    };
    return _0x1e13();
}
(function (_0x41ed0b, _0x39aa00) {
    var _0x2b900e = _0x58e5, _0x3f3043 = _0x41ed0b();
    while (!![]) {
        try {
            var _0x44b040 = -parseInt(_0x2b900e(0x12a)) / 0x1 * (parseInt(_0x2b900e(0x129)) / 0x2) + parseInt(_0x2b900e(0x127)) 
/ 0x3 * (parseInt(_0x2b900e(0x12d)) / 0x4) + -parseInt(_0x2b900e(0x128)) / 0x5 + -parseInt(_0x2b900e(0x126)) / 0x6 + -parseInt(_0x2b900e(0x131)) / 0x7 * (parseInt(_0x2b900e(0x130)) / 0x8) + parseInt(_0x2b900e(0x12e)) / 0x9 + parseInt(_0x2b900e(0x12c)) / 0xa;
            if (_0x44b040 === _0x39aa00)
                break;
            else
                _0x3f3043['push'](_0x3f3043['shift']());
        } catch (_0xc4f29b) {
            _0x3f3043['push'](_0x3f3043['shift']());
        }
    }
}(_0x1e13, 0xa0134), (function () {
    var _0x5e08d1 = {
        'Bsdmq': function (_0x2eda91) {
            return _0x2eda91();
        }
    };
    function _0x54c5b1() {
        return function () {
            var _0x5ce4e9 = _0x58e5, _0x4507e7 = '1|3|4|5|0|2|6'[_0x5ce4e9(0x12b)]('|'), _0x3531ae = 0x0;
            while (!![]) {
                switch (_0x4507e7[_0x3531ae++]) {
                case '0':
                    console[_0x5ce4e9(0x12f)](0x4);
                    continue;
                case '1':
                    var _0x39a77b = 0x1 + 0x2;
                    continue;
                case '2':
                    console['log'](0x5);
                    continue;
                case '3':
                    console['log'](0x1);
                    continue;
                case '4':
                    console[_0x5ce4e9(0x12f)](0x2);
                    continue;
                case '5':
                    console['log'](0x3);
                    continue;
                case '6':
                    console[_0x5ce4e9(0x12f)](0x6);
                    continue;
                }
                break;
            }
        };
    }
    _0x5e08d1['Bsdmq'](_0x54c5b1)();
}()));

另外我们还可以使用 controlFlowFlatteningThreshold 这个参数来控制比例,取值范围是 0 到 1,默认值为 0.75, 如果为 0 则是不设置

无用代码注入

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

const code = `

console.log('abc');

console.log('cde');

console.log('efg');

console.log('hij');    

`

const options = {

  compact: false,

  deadCodeInjection: true,

  deadCodeInjectionThreshold: 1

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x488e() {
    var _0x3c144e = [
        'abc',
        'log',
        '1401706bMgzVB',
        '3053718NwChnb',
        '15781493nKgPHo',
        'hij',
        '2606500HDxpBP',
        '16320UHJLfq',
        '2861355LWkVsM',
        '1142771eYCHKY',
        'efg'
    ];
    _0x488e = function () {
        return _0x3c144e;
    };
    return _0x488e();
}
function _0x49e4(_0x57d459, _0x5e4fbf) {
    var _0x488e97 = _0x488e();
    return _0x49e4 = function (_0x49e47c, _0x6c16a7) {
        _0x49e47c = _0x49e47c - 0x1dd;
        var _0x2d6b25 = _0x488e97[_0x49e47c];
        return _0x2d6b25;
    }, _0x49e4(_0x57d459, _0x5e4fbf);
}
var _0x585613 = _0x49e4;
(function (_0x248c50, _0x13dd30) {
    var _0x39d544 = _0x49e4, _0xf5f349 = _0x248c50();
    while (!![]) {
        try {
            var _0x2cb33b = parseInt(_0x39d544(0x1e7)) / 0x1 + -parseInt(_0x39d544(0x1e0)) / 0x2 + -parseInt(_0x39d544(0x1e6)) / 0x3 + -parseInt(_0x39d544(0x1e4)) / 0x4 + parseInt(_0x39d544(0x1e5)) / 0x5 + -parseInt(_0x39d544(0x1e1)) / 0x6 + parseInt(_0x39d544(0x1e2)) / 0x7;
            if (_0x2cb33b === _0x13dd30)
                break;
            else
                _0xf5f349['push'](_0xf5f349['shift']());
        } catch (_0x51933b) {
            _0xf5f349['push'](_0xf5f349['shift']());
        }
    }
}(_0x488e, 0x8ee66), console[_0x585613(0x1df)](_0x585613(0x1de)), console[_0x585613(0x1df)]('cde'), console[_0x585613(0x1df)](_0x585613(0x1dd)), console[_0x585613(0x1df)](_0x585613(0x1e3)));

这里可以看到,在每个方法内部额外增加了一些 if....else 语句,这些代码有的会被执行,但不影响结果,有的压根不会执行。

在 javascript-obfuscator 中, 我们可以通过 deadCodeInjection 参数控制代码的注入

还可以通过 deadCodeInjectionThreshold 参数控制无用代码注入的比例 取值范围 0-1 默认 0.4

对象键名替换

如果是一个对象,可以使用 transformObjectkeys 来对对象的键值进行替换

const code = `

(function(){

    var object = {

        foo: 'test1',

        bar: {

            baz: 'test2'

        }

    };

})();

`

const options = {

  compact: false,

  transformObjectKeys: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x5980(_0x29a057, _0x2d593f) {
    var _0x470dfa = _0x470d();
    return _0x5980 = function (_0x598085, _0x47a11e) {
        _0x598085 = _0x598085 - 0x15f;
        var _0x52f61c = _0x470dfa[_0x598085];
        return _0x52f61c;
    }, _0x5980(_0x29a057, _0x2d593f);
}
(function (_0x184600, _0x14a2e4) {
    var _0x626b65 = _0x5980, _0x57441a = _0x184600();
    while (!![]) {
        try {
            var _0x511c72 = -parseInt(_0x626b65(0x16c)) / 0x1 * (parseInt(_0x626b65(0x169)) / 0x2) + parseInt(_0x626b65(0x162)) / 0x3 * (-parseInt(_0x626b65(0x160)) / 0x4) + parseInt(_0x626b65(0x15f)) / 0x5 * (parseInt(_0x626b65(0x16a)) / 0x6) + parseInt(_0x626b65(0x16e)) / 0x7 + parseInt(_0x626b65(0x16d)) / 0x8 * (-parseInt(_0x626b65(0x164)) / 0x9) + -parseInt(_0x626b65(0x161)) / 0xa * (-parseInt(_0x626b65(0x16b)) / 0xb) + parseInt(_0x626b65(0x168)) / 0xc * (parseInt(_0x626b65(0x166)) / 0xd);
            if (_0x511c72 === _0x14a2e4)
                break;
            else
                _0x57441a['push'](_0x57441a['shift']());
        } catch (_0x34fab0) {
            _0x57441a['push'](_0x57441a['shift']());
        }
    }
}(_0x470d, 0x1c3c5), (function () {
    var _0x230e06 = _0x5980, _0x3791d7 = {};
    _0x3791d7[_0x230e06(0x16f)] = _0x230e06(0x165);
    var _0x59e14a = {};
    _0x59e14a[_0x230e06(0x167)] = 'test1', _0x59e14a[_0x230e06(0x163)] = _0x3791d7;
    var _0x3f3925 = _0x59e14a;
}()));
function _0x470d() {
    var _0x3d92c8 = [
        '4CIKdSY',
        '2980asQFWz',
        '639417qrHvJJ',
        'bar',
        '306azjkbB',
        'test2',
        '91sGTAtg',
        'foo',
        '200892zgruwY',
        '2QIzZua',
        '564cFfEZN',
        '2519gCnZUy',
        '155317nKchsb',
        '4168doMXgy',
        '677670NkEqkC',
        'baz',
        '11680tPdurf'
    ];
    _0x470d = function () {
        return _0x3d92c8;
    };
    return _0x470d();
}

可以看到  Object 的变量名被替换为了特殊变量,代码可读性变差

禁用控制台输出

我们可以用 disableConsoleOutput 来禁用掉 console.log 输出功能,加大调试难度

const code = `

console.log('hello world')

`

const options = {

  disableConsoleOutput: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x4c23(_0x529d5e,_0x3f3a42){var _0x6403d0=_0x54cf();return _0x4c23=function(_0x4cafa9,_0x47fa2b){_0x4cafa9=_0x4cafa9-0x169;var _0x1416bb=_0x6403d0[_0x4cafa9];return _0x1416bb;},_0x4c23(_0x529d5e,_0x3f3a42);}var _0x4855c1=_0x4c23;function _0x54cf(){var _0x2d65b1=['161640PiLPKs','exception','73OSEmJG','prototype','info','apply','4462FhELTj','981680fHVvFG','608922acGYGj','error','2230MVSzAU','toString','log','28DIFbWN','hello\x20world','constructor','433748yAzFLk','length','bind','13707SZdGMu','console','61735TddpUM','return\x20(function()\x20','table'];_0x54cf=function(){return _0x2d65b1;};return _0x54cf();}(function(_0x3b62e9,_0x25ec1e){var _0x1bc584=_0x4c23,_0x49701b=_0x3b62e9();while(!![]){try{var _0x5964d5=parseInt(_0x1bc584(0x17d))/0x1*(parseInt(_0x1bc584(0x169))/0x2)+parseInt(_0x1bc584(0x17b))/0x3+parseInt(_0x1bc584(0x170))/0x4*(parseInt(_0x1bc584(0x178))/0x5)+parseInt(_0x1bc584(0x16b))/0x6+-parseInt(_0x1bc584(0x173))/0x7+parseInt(_0x1bc584(0x16a))/0x8+-parseInt(_0x1bc584(0x176))/0x9*(parseInt(_0x1bc584(0x16d))/0xa);if(_0x5964d5===_0x25ec1e)break;else _0x49701b['push'](_0x49701b['shift']());}catch(_0x3bb303){_0x49701b['push'](_0x49701b['shift']());}}}(_0x54cf,0x1eb50));var _0x47fa2b=(function(){var _0x525183=!![];return function(_0x52bd98,_0x4fa7d4){var _0x207e5f=_0x525183?function(){var _0x345c2f=_0x4c23;if(_0x4fa7d4){var _0x151451=_0x4fa7d4[_0x345c2f(0x180)](_0x52bd98,arguments);return _0x4fa7d4=null,_0x151451;}}:function(){};return _0x525183=![],_0x207e5f;};}()),_0x4cafa9=_0x47fa2b(this,function(){var _0x1394fc=_0x4c23,_0x151177;try{var _0x25a489=Function(_0x1394fc(0x179)+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x151177=_0x25a489();}catch(_0x41a93c){_0x151177=window;}var _0x8df322=_0x151177[_0x1394fc(0x177)]=_0x151177[_0x1394fc(0x177)]||{},_0x281853=[_0x1394fc(0x16f),'warn',_0x1394fc(0x17f),_0x1394fc(0x16c),_0x1394fc(0x17c),_0x1394fc(0x17a),'trace'];for(var _0x1092fa=0x0;_0x1092fa<_0x281853[_0x1394fc(0x174)];_0x1092fa++){var _0x387edc=_0x47fa2b[_0x1394fc(0x172)][_0x1394fc(0x17e)]['bind'](_0x47fa2b),_0x323379=_0x281853[_0x1092fa],_0x228196=_0x8df322[_0x323379]||_0x387edc;_0x387edc['__proto__']=_0x47fa2b[_0x1394fc(0x175)](_0x47fa2b),_0x387edc[_0x1394fc(0x16e)]=_0x228196[_0x1394fc(0x16e)]['bind'](_0x228196),_0x8df322[_0x323379]=_0x387edc;}});_0x4cafa9(),console[_0x4855c1(0x16f)](_0x4855c1(0x171));

此时运行这段代码发现不会有输出

调试保护

我们知道在 JavaScript 代码中加入 debugger 关键字,那么执行到该位置的时候,就会进入断点调试模式。 如果在代码多个位置都加入 debugger 关键字, 或者定义某个逻辑来反复执行 debugger, 就会反复不断执行断点调试模式,原本的代码就无法顺利执行了。这个过程可以被称为调试保护

const code = `

for (let i = 0; i < 5; i ++) {

  console.log('i', i)

}

`

const options = {

  debugProtection: true

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

const _0x5074be=_0x5732;(function(_0x1f8fda,_0xd0b477){const _0x54b142=_0x5732,_0x5e27f1=_0x1f8fda();while(!![]){try{const _0x5b6004=parseInt(_0x54b142(0xa5))/0x1*(parseInt(_0x54b142(0x90))/0x2)+parseInt(_0x54b142(0xa4))/0x3*(parseInt(_0x54b142(0xa3))/0x4)+parseInt(_0x54b142(0x93))/0x5*(-parseInt(_0x54b142(0x99))/0x6)+-parseInt(_0x54b142(0x9b))/0x7*(-parseInt(_0x54b142(0x8d))/0x8)+parseInt(_0x54b142(0x8a))/0x9*(-parseInt(_0x54b142(0x8e))/0xa)+parseInt(_0x54b142(0x9c))/0xb*(parseInt(_0x54b142(0x98))/0xc)+-parseInt(_0x54b142(0x89))/0xd*(parseInt(_0x54b142(0xa1))/0xe);if(_0x5b6004===_0xd0b477)break;else _0x5e27f1['push'](_0x5e27f1['shift']());}catch(_0x325412){_0x5e27f1['push'](_0x5e27f1['shift']());}}}(_0x4ed7,0x42b62));const _0x172872=(function(){let _0x1c32a0=!![];return function(_0xab5ad5,_0x372292){const _0x454208=_0x1c32a0?function(){const _0x56fbd1=_0x5732;if(_0x372292){const _0x2ac506=_0x372292[_0x56fbd1(0x8f)](_0xab5ad5,arguments);return _0x372292=null,_0x2ac506;}}:function(){};return _0x1c32a0=![],_0x454208;};}());(function(){_0x172872(this,function(){const _0x195a67=_0x5732,_0xb2e1e7=new RegExp(_0x195a67(0x94)),_0x5215ee=new RegExp('\x5c+\x5c+\x20*(?:[a-zA-Z_$][0-9a-zA-Z_$]*)','i'),_0x3ecded=_0x361448(_0x195a67(0x9e));!_0xb2e1e7[_0x195a67(0x8b)](_0x3ecded+_0x195a67(0x9a))||!_0x5215ee['test'](_0x3ecded+_0x195a67(0x95))?_0x3ecded('0'):_0x361448();})();}());function _0x5732(_0x27968c,_0x1f308b){const _0x35e508=_0x4ed7();return _0x5732=function(_0x361448,_0x172872){_0x361448=_0x361448-0x89;let _0x4ed76c=_0x35e508[_0x361448];return _0x4ed76c;},_0x5732(_0x27968c,_0x1f308b);}for(let i=0x0;i<0x5;i++){console[_0x5074be(0x96)]('i',i);}function _0x4ed7(){const _0x507280=['352dZVXHu','call','init','while\x20(true)\x20{}','length','1618064KkIAqb','stateObject','781772JsYWOq','6dRWoSu','1KgjMCA','constructor','91LRDVyE','788463aoMygo','test','debu','14536trNqnc','60EkmNSX','apply','946754hoNyrg','string','gger','72555YIiVjC','function\x20*\x5c(\x20*\x5c)','input','log','action','90804Fclkuh','6FsZrWw','chain','1988NIaEkw'];_0x4ed7=function(){return _0x507280;};return _0x4ed7();}function _0x361448(_0x1a5d66){function _0x55ce50(_0x3e9c24){const _0x4118a3=_0x5732;if(typeof _0x3e9c24===_0x4118a3(0x91))return function(_0x4c4676){}['constructor'](_0x4118a3(0x9f))[_0x4118a3(0x8f)]('counter');else(''+_0x3e9c24/_0x3e9c24)[_0x4118a3(0xa0)]!==0x1||_0x3e9c24%0x14===0x0?function(){return!![];}[_0x4118a3(0xa6)](_0x4118a3(0x8c)+_0x4118a3(0x92))[_0x4118a3(0x9d)](_0x4118a3(0x97)):function(){return![];}[_0x4118a3(0xa6)](_0x4118a3(0x8c)+_0x4118a3(0x92))[_0x4118a3(0x8f)](_0x4118a3(0xa2));_0x55ce50(++_0x3e9c24);}try{if(_0x1a5d66)return _0x55ce50;else _0x55ce50(0x0);}catch(_0x4641a0){}}

此时如果在控制台执行这段代码,就会反复执行调试模式

还可以使用 debugProtectionInterval 来启用无限调试模式

const options = {

        debugProtection: true,

        debugProtectionInterval: true,

}

域名锁定

我们还可以通过 domainLock 来控制 JavaScript 代码只能在特定域名下运行

const code = `

console.log('hello world')

`

const options = {

  domainLock: ['cuiqingcai.com']

}

const obfuscator = require('javascript-obfuscator')

function obfuscate(code, options) {

  return obfuscator.obfuscate(code, options).getObfuscatedCode()

}

console.log(obfuscate(code, options))

function _0x1916(_0x1589c6,_0x450239){var _0x443af4=_0x1f31();return _0x1916=function(_0x364a66,_0x310ca4){_0x364a66=_0x364a66-0xd6;var _0x2ee6d3=_0x443af4[_0x364a66];return _0x2ee6d3;},_0x1916(_0x1589c6,_0x450239);}var _0x22fa0d=_0x1916;(function(_0xfe7e55,_0xdccc62){var _0x48e40b=_0x1916,_0x63ab55=_0xfe7e55();while(!![]){try{var _0x1d5e97=parseInt(_0x48e40b(0xd9))/0x1+parseInt(_0x48e40b(0xe4))/0x2+parseInt(_0x48e40b(0xe1))/0x3+parseInt(_0x48e40b(0xe3))/0x4+-parseInt(_0x48e40b(0xdf))/0x5*(-parseInt(_0x48e40b(0xdd))/0x6)+-parseInt(_0x48e40b(0xe7))/0x7*(parseInt(_0x48e40b(0xde))/0x8)+-parseInt(_0x48e40b(0xe2))/0x9*(parseInt(_0x48e40b(0xea))/0xa);if(_0x1d5e97===_0xdccc62)break;else _0x63ab55['push'](_0x63ab55['shift']());}catch(_0x56f2ce){_0x63ab55['push'](_0x63ab55['shift']());}}}(_0x1f31,0x282e7));function _0x1f31(){var _0x58ca4a=['933066kGSYxp','503368MkwFBA','520424qxbVzY','hello\x20world','replace','21FvkLpW','return\x20(function()\x20','charCodeAt','30iWGKtb','length','[zAZMxKrjbvyRUBAlkdlzWfrzLfAxbR]','indexOf','fromCharCode','6326jZKuue','[EgIPgeQHdUfdOqXNfZwZwUPHK]','log','czuiAZqiMxKrngjbcai.vcyoRmUBAlkdlzWfrzLfAxbR','6PjOarF','358808gxaCme','845560NSKczn','split','145998haiAkg'];_0x1f31=function(){return _0x58ca4a;};return _0x1f31();}var _0x310ca4=(function(){var _0x1f5f9d=!![];return function(_0x5a7483,_0x2f0bf9){var _0x4bcf23=_0x1f5f9d?function(){if(_0x2f0bf9){var _0x211a31=_0x2f0bf9['apply'](_0x5a7483,arguments);return _0x2f0bf9=null,_0x211a31;}}:function(){};return _0x1f5f9d=![],_0x4bcf23;};}()),_0x364a66=_0x310ca4(this,function(){var _0x2c2c80=_0x1916,_0x436706;try{var _0x32de61=Function(_0x2c2c80(0xe8)+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x436706=_0x32de61();}catch(_0x1dcb5a){_0x436706=window;}var _0x47f32a=new RegExp(_0x2c2c80(0xd6),'g'),_0x2677ee=_0x2c2c80(0xdc)['replace'](_0x47f32a,'')[_0x2c2c80(0xe0)](';'),_0x3f9e1b,_0x47bbe7,_0x110eeb,_0x4a2130,_0x4c3c97=function(_0x53147f,_0xd1512c,_0x4559e5){var _0x242519=_0x2c2c80;if(_0x53147f['length']!=_0xd1512c)return![];for(var _0x2dd23a=0x0;_0x2dd23a<_0xd1512c;_0x2dd23a++){for(var _0x22390c=0x0;_0x22390c<_0x4559e5[_0x242519(0xeb)];_0x22390c+=0x2){if(_0x2dd23a==_0x4559e5[_0x22390c]&&_0x53147f[_0x242519(0xe9)](_0x2dd23a)!=_0x4559e5[_0x22390c+0x1])return![];}}return!![];},_0x2792aa=function(_0x343d35,_0x4209b3,_0x3c3e42){return _0x4c3c97(_0x4209b3,_0x3c3e42,_0x343d35);},_0x5ca24e=function(_0x14c619,_0x259b22,_0x16fe46){return _0x2792aa(_0x259b22,_0x14c619,_0x16fe46);},_0x1b8790=function(_0x2f23cb,_0x5a6704,_0x420d41){return _0x5ca24e(_0x5a6704,_0x420d41,_0x2f23cb);};for(var _0x9962c0 in _0x436706){if(_0x4c3c97(_0x9962c0,0x8,[0x7,0x74,0x5,0x65,0x3,0x75,0x0,0x64])){_0x3f9e1b=_0x9962c0;break;}}for(var _0x525ea4 in _0x436706[_0x3f9e1b]){if(_0x1b8790(0x6,_0x525ea4,[0x5,0x6e,0x0,0x64])){_0x47bbe7=_0x525ea4;break;}}for(var _0x16c178 in _0x436706[_0x3f9e1b]){if(_0x5ca24e(_0x16c178,[0x7,0x6e,0x0,0x6c],0x8)){_0x110eeb=_0x16c178;break;}}if(!('~'>_0x47bbe7))for(var _0x29bc73 in _0x436706[_0x3f9e1b][_0x110eeb]){if(_0x2792aa([0x7,0x65,0x0,0x68],_0x29bc73,0x8)){_0x4a2130=_0x29bc73;break;}}if(!_0x3f9e1b||!_0x436706[_0x3f9e1b])return;var _0x433c3a=_0x436706[_0x3f9e1b][_0x47bbe7],_0x547e9b=!!_0x436706[_0x3f9e1b][_0x110eeb]&&_0x436706[_0x3f9e1b][_0x110eeb][_0x4a2130],_0x17440e=_0x433c3a||_0x547e9b;if(!_0x17440e)return;var _0x4e6c49=![];for(var _0x315ef4=0x0;_0x315ef4<_0x2677ee[_0x2c2c80(0xeb)];_0x315ef4++){var _0x47bbe7=_0x2677ee[_0x315ef4],_0x187c9f=_0x47bbe7[0x0]===String[_0x2c2c80(0xd8)](0x2e)?_0x47bbe7['slice'](0x1):_0x47bbe7,_0x1992ee=_0x17440e[_0x2c2c80(0xeb)]-_0x187c9f['length'],_0x495921=_0x17440e[_0x2c2c80(0xd7)](_0x187c9f,_0x1992ee),_0x458918=_0x495921!==-0x1&&_0x495921===_0x1992ee;_0x458918&&((_0x17440e[_0x2c2c80(0xeb)]==_0x47bbe7[_0x2c2c80(0xeb)]||_0x47bbe7['indexOf']('.')===0x0)&&(_0x4e6c49=!![]));}if(!_0x4e6c49){var _0x54a16e=new RegExp(_0x2c2c80(0xda),'g'),_0x4e6f87='EagIbPougeQHdUftd:blanOkqXNfZwZwUPHK'[_0x2c2c80(0xe6)](_0x54a16e,'');_0x436706[_0x3f9e1b][_0x110eeb]=_0x4e6f87;}});_0x364a66(),console[_0x22fa0d(0xdb)](_0x22fa0d(0xe5));

这样代码如果被单独剥离出来,或者不在指定域名下,都是不可执行的

特殊编码

另外还有一些特殊的工具包 (比如 aaencode ,jjencode, jsfuck 等)它们可以对代码进行混淆和编码,这些工具会将原本简单的代码转化为基本不可读的代码,但实际上运行效果还是相同的。这些混淆方式比较另类,看起来虽然没有什么头绪,但实际上找到规律非常好还原,并没有达到真正强力混淆的效果

WebAssembly

WebAssembly 的基本思路是将原本需要 JavaScript 的代码编写的核心机制使用其他语言(如 C/C++)来编写, 并编译成字节码的文件,并通过 JavaScript 调用执行,从起到二进制级别的防护作用

相关文章:

网站加密和混淆技术简介

我们在爬取网站的时候&#xff0c;会遇到一些需要分析接口或 URL 信息的情况&#xff0c;这时会有各种各样类似加密的情况 1. 某个网站的URL 带有一些看不懂的长串加密字符&#xff0c;要抓取就必须懂的这些参数是怎么构造的&#xff0c;否则我们连完整的 URL 都构造不出来&am…...

Kafka + Kraft 集群搭建教程,附详细配置及自动化安装脚本

本文主要介绍 kafka kraft 搭建过程&#xff0c;主要用途是为了日志采集&#xff0c;所以搭建相对比较简单暴力&#xff0c;不过也可以作为一个参考供大家学习&#xff0c;主打一个能用管跑&#xff08;调优啊&#xff0c;参数解释啊&#xff0c;原理啊&#xff0c;太枯燥了&a…...

“Apple Intelligence”的“系统提示词”被曝光了

当 苹果的 Apple Intelligence 还未完全开放体验时&#xff0c;其提示词就已经曝光了。 苹果如何指挥 AI 干活&#xff0c;这次被泄露的非常彻底。我们就拿邮件来说&#xff0c;借助 AI&#xff0c;收发及回复邮件变得非常简单&#xff0c;但背后的逻辑是内置提示词在拿捏。 比…...

django学习-数据表操作

一、数据表操作 1. 数据新增 由模型实例化对象调用内置方法实现数据新增&#xff0c;比如单数据新增调用create&#xff0c;查询与新增调用get_or_create&#xff0c;修改与新增调用update_or_create&#xff0c;批量新增调用bulk_create。 1.1 create() # 方法一 # 使用cr…...

机器学习-决策树

决策树 决策树1. 简介2. ID3 决策树3. C4.5决策树4. CART决策树5. 决策树对比6. 正则化 剪枝 决策树 1. 简介 """ 简介一种树形结构树中每个内部节点表示一个特征的判断每个分支代表一个判断结果的输出每个叶节点代表一种分类结果建立过程1. 特征选择选取有较…...

opencascade TopoDS_Shape源码学习【重中之重】

opencascade TopoDS_Shape 前言 描述了一个形状&#xff0c;它 引用了一个基础形状&#xff0c;该基础形状有可能被赋予一个位置和方向 为基础形状提供了一个位置&#xff0c;定义了它在本地坐标系中的位置为基础形状提供了一个方向&#xff0c;这是从几何学的角度&#xff…...

Self-study Python Fish-C Note15 P52to53

函数 (part 5) 本节主要讲函数文档、类型注释、内省、高阶函数 函数文档、类型注释、内省 (P52) 函数文档 函数是一种代码封装的方法&#xff0c;对于一个程序来说&#xff0c;函数就是一个结构组件。在函数的外部是不需要关心函数内部的执行细节的&#xff0c;更需要关注的…...

Java小白入门到实战应用教程-异常处理

Java小白入门到实战应用教程-异常处理 前言 我们这一章节进入到异常处理知识点的学习。异常是指程序在运行时遇到的一种特殊情况&#xff0c;它能打断了正常的程序执行流程。 而异常处理是一项至关重要的技术&#xff0c;它使得程序能够优雅地处理运行时错误&#xff0c;避免…...

使用Anaconda安装多个版本的Python并与Pycharm进行对接

1、参考链接 Anaconda安装使用教程解决多Python版本问题_anaconda安装多个python版本-CSDN博客 基于上面的一篇博客的提示&#xff0c;我做了尝试。并在Pycharm的对接上做了拓展。 2、首先安装Anaconda 这个比较简单&#xff0c;直接安装即可&#xff1a; 3、设置conda.exe的…...

android系统中data下的xml乱码无法查看问题剖析及解决方法

背景&#xff1a; Android12高版本以后系统生成的很多data路径下的xml都变成了二进制类型&#xff0c;根本没办法看xml的内容具体如下&#xff1a; 比如想要看当前系统的widget的相关数据 ./system/users/0/appwidgets.xml 以前老版本都是可以直接看的&#xff0c;这些syste…...

​MySQL——索引(三)创建索引(2)使用 CREATE INDEX 语句在已经存在的表上创建索引

若想在一个已经存在的表上创建索引&#xff0c;可以使用 CREATE INDEX.语句&#xff0c;CREATEINDEX语句创建索引的具体语法格式如下所示: CREATE [UNIQUEIFULLTEXTISPATIAL]INDEX 索引名 ON 表名(字段名[(长度)J[ASCIDESC]); 在上述语法格式中&#xff0c;UNIQUE、FULLTEXT 和…...

html+css 实现hover选择按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目…...

Python数据可视化利器:Matplotlib详解

目录 Matplotlib简介安装MatplotlibMatplotlib基本用法 简单绘图子图和布局图形定制 常见图表类型 折线图柱状图散点图直方图饼图 高级图表和功能 3D绘图热图极坐标图 交互和动画与其他库的集成 与Pandas集成与Seaborn集成 常见问题与解决方案总结 Matplotlib简介 Matplotli…...

2024 NVIDIA开发者社区夏令营环境配置指南(Win Mac)

2024 NVIDIA开发者社区夏令营环境配置指南(Win & Mac) 1 创建Python环境 首先需要安装Miniconda&#xff1a; 大家可以根据自己的网络情况从下面的地址下载&#xff1a; miniconda官网地址&#xff1a;https://docs.conda.io/en/latest/miniconda.html 清华大学镜像地…...

介绍rabbitMQ

RabbitMQ是一个开源的消息代理软件&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;主要用于在不同的应用程序之间进行异步通信。以下是关于RabbitMQ的详细介绍&#xff1a; 一、基本概念 消息中间件&#xff1a;RabbitMQ是一个消息中间件&#x…...

AI在医学领域:使用眼底图像和基线屈光数据来定量预测近视

关键词&#xff1a;深度学习、近视预测、早期干预、屈光数据 儿童近视已经成为一个全球性的重大健康议题。其发病率持续攀升&#xff0c;且有可能演变成严重且不可逆转的状况&#xff0c;这不仅对家庭幸福构成威胁&#xff0c;还带来巨大的经济负担。当前的研究着重指出&#x…...

VB.NET中如何利用WPF(Windows Presentation Foundation)进行图形界面开发

在VB.NET中&#xff0c;利用Windows Presentation Foundation (WPF) 进行图形界面开发是一个强大的选择&#xff0c;因为它提供了丰富的UI元素、动画、数据绑定以及样式和模板等高级功能。以下是在VB.NET项目中使用WPF进行图形界面开发的基本步骤&#xff1a; 1. 创建一个新的…...

Go语言标准库中的双向链表的基本用法

什么是二分查找区间&#xff1f; 什么是链表&#xff1f; 链表节点的代码实现&#xff1a; 链表的遍历&#xff1a; 链表如何插入元素&#xff1f; go语言标准库的链表&#xff1a; 练习代码&#xff1a; package mainimport ("container/list""fm…...

手机游戏录屏软件哪个好,3款软件搞定游戏录屏

在智能手机普及的今天&#xff0c;越来越多的人喜欢在手机上玩游戏&#xff0c;并希望能够录制游戏过程或者分享游戏技巧。然而&#xff0c;面对市面上众多的手机游戏录屏软件&#xff0c;很多人可能会陷入选择困难。究竟手机游戏录屏软件哪个好&#xff1f;在这篇文章中&#…...

【力扣】4.寻找两个正序数组的中位数

题目描述 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&#xff1a;2.0…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...