JavaScript 代码混淆技术

141 阅读6分钟

image.png

嗨小伙伴们,今天给大家带来一篇超赞的翻译文章!本文是我翻译的,原文链接在这里哦:Javascript obfuscation techniques by example

我的目的是帮助大家更好地理解原文的内容。如果我们翻译的不够准确或者有疑问的地方,欢迎随时提出哦~

废话不多说,让我们一起来看看这篇翻译文章吧!


有时,当你在抓取一些网站时,你会看到 JavaScript 代码,它看起来一团糟,无法阅读——无论你怎么眯着眼睛,你都无法理解它,那是因为它被混淆了。代码混淆是一种转换,旨在使代码难以阅读和防止逆向工程。许多网站利用 JavaScript 混淆来让抓取工具或自动化开发人员感到为难。

给出以下非常简单的 Javascript 语句,它作为后面演示的示例代码:

console.log("Hello, world! " + 123);

我们将讨论混淆它的各种方法。为了清楚起见,我们将一次介绍一种混淆技术,并提供一个示例,说明每种技术如何以一种不可读的形式更改此代码。然而,应该注意的是,通常在反抓取网站中使用多种混淆方法。

演示过程中将尝试一些混淆 JS 代码的在线工具。

字符串编码

十六进制字符串编码

打开 Javascript Obfuscator 网站关闭除 「Encode Strings」之外的所有复选框,混淆示例代码:

输出结果如下:

console["\x6C\x6F\x67"]("\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21\x20"+ 123)

硬编码字符串的每个 ASCII 字符都被转换为十六进制形式,可以在计算机程序上进行验证。这很简单因为混淆隐藏东西的方式很弱,在 JavaScript REPL 可以简单地还原:

$ node
Welcome to Node.js v18.5.0.
Type ".help" for more information.
> "\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21\x20"
'Hello, world! '
> "\x6C\x6F\x67"
'log'

另外,我们注意到,在代码的混淆形式中使用了方括号符号 ——console.log 变成了 console["log"]。因为后一种相对前一种能使用变量,这也是 JS 混淆常用的伎俩。

八进制字符串编码

除了十六进制,还可以用八进制进行编码,比如:

console["\154\157\147"]("\150\145\154\154\157\54\40\167\157\162\154\144\41\40" + "\40\61\62\63")

运算结果:

image.png

请注意,八进制转义符在 ES5 中已被弃用,此外,它们在严格模式下会产生语法错误。

Unicode 字符串编码

除了上面两种,我们还可以进行 Unicode 编码,比如:

console["\u006c\u006f\u0067"]("\u0048\u0065\u006c\u006c\u006f\u002c\u0020\u0057\u006f\u0072\u006c\u0064\u0021" + "\u0031\u0032\u0033")

image.png

关于 JS 编码详情可以学习文章:JavaScript character escape sequences

字符串数组映射

现在让我们取消选中「Encode Strings」并选中「Move Strings」。单击混淆按钮会为我们提供以下代码:

var _0xcd26=["Hello, world! ","log"];console[_0xcd26[1]](_0xcd26[0]+ 123)

我们可以看到代码现在有一个字符串数组,通过在各种索引处引用数组来间接使用这些字符串。

死代码注入

在不改变功能的情况下,可以注入死代码或无法访问的代码,使 Javascript 源代码更大,更难阅读。让我们用 defendjs 试试

$ defendjs --input hello.js --features dead_code --output .

这将使我们的 JS 代码变成了一团糟:

(function () {
    function a(a, d) {
        var b = new Array(0);;
        var c = arguments;
        while (true)
            try {
                switch (a) {
                case 15229:
                    return;
                case 21176:
                    function e(a, b) {
                        return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
                    }
                    function f() {
                        var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
                        var b = function () {
                            return a.apply(this, c.concat(Array.prototype.slice.call(arguments)));
                        };
                        b.prototype = a.prototype;
                        return b;
                    }
                    function g(a, b) {
                        return Array.prototype.slice.call(a, b);
                    }
                    function h(b) {
                        var c = {};
                        for (var a = 0; a < b.length; a += 2) {
                            c[b[a]] = b[a + 1];
                        }
                        return c;
                    }
                    function i(a) {
                        return a.map(function (a) {
                            return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
                        }).join('');
                    }
                    function j() {
                        return String.fromCharCode.apply(null, arguments);
                    }
                    console.log('Hello, world! ' + 123);
                    a = 15229;
                    break;
                }
            } catch (b) {
                $$defendjs$tobethrown = null;
                switch (a) {
                default:
                    throw b;
                }
            }
    }
    a(21176, {});
}())

我们可以看到我们的原始语句,但它现在嵌套得很深了。在顶层,有一个叫做 IIFE 的东西——立即调用函数表达式——一个在单个语句中定义和调用的 JS 函数。这个函数有两个语句:

  1. a()带有两个参数的函数声明。
  2. 调用a()第一个参数值为 21176 的函数。

如果我们阅读函数的代码,a()我们可以看到第二个参数被忽略,并且只有一条代码路径指向我们要执行的代码console.log(),并且它周围有相当多的代码永远无法执行 - 一些函数嵌套在 switch语句深处并且 try/catch 没有任何出错的可能。此外,它在开始时声明了几个永远不会被使用的变量。

控制流混淆

我们的死代码示例,还演示了另外一种可以使控制流变得混乱的方式 - 控制流混淆。

在无限循环代码 while 语句中,包含 try-catch 结构。下面 try 是一个包含两种情况的 switch 语句:一种用于从函数中返回,另一种用于调用 log 函数,并为变量设置一个值,然后退出循环代码。这引入了一个令人困惑的、迂回的代码路径,它在功能上与我们原来的单行代码相同。

范围混淆

让我们重新运行代码并使用 DefendJS 的 scope 功能:

$ cp hello.js scope.js
$ defendjs --input scope.js --features scope --output .

这为我们提供了以下代码:

(function () {
    {
        {
            function b(a, b) {
                return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
            }
            function c() {
                var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
                var b = function () {
                    return a.apply(this, c.concat(Array.prototype.slice.call(arguments)));
                };
                b.prototype = a.prototype;
                return b;
            }
            function d(a, b) {
                return Array.prototype.slice.call(a, b);
            }
            function e(b) {
                var c = {};
                for (var a = 0; a < b.length; a += 2) {
                    c[b[a]] = b[a + 1];
                }
                return c;
            }
            function f(a) {
                return a.map(function (a) {
                    return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
                }).join('');
            }
            function g() {
                return String.fromCharCode.apply(null, arguments);
            }
        }
        var a = [];
        console.log('Hello, world! ' + 123);
    }
}())

这种技术可能看起来像前一种技术的简单版本,但有一个关键的区别:它引入了多个相同的词法作用域范围。例如,a可能是最内层作用域中第一个函数的参数,或者它可以是第二个函数中的变量,甚至是与我们的日志语句相同作用域中的变量。在我们的简单示例中,很容易看出这一点,因为最内层作用域中的任何函数都没有在任何地方被调用,但在现实世界的情况下不会那么简单。

字面量的重新计算

让我们试试 DefendsJS 的另一个特性:

$ cp hello.js literals.js
$ defendjs --input literals.js --features literals --output .

结果代码是:

(function () {
    function c() {
        var c = arguments;
        var b = [];
        b[1] = '';
        b[1] += a(72);
        b[1] += a(101, 108, 108, 111);
        b[1] += a(44, 32, 119, 111);
        b[1] += a(114, 108, 100, 33);
        b[1] += a(32);
        return b[1];
    }
    {
        {
            function e(a, b) {
                return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
            }
            function d() {
                var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
                var b = function () {
                    return a.apply(this, c.concat(Array.prototype.slice.call(arguments)));
                };
                b.prototype = a.prototype;
                return b;
            }
            function f(a, b) {
                return Array.prototype.slice.call(a, b);
            }
            function g(b) {
                var c = {};
                for (var a = 0; a < b.length; a += 2) {
                    c[b[a]] = b[a + 1];
                }
                return c;
            }
            function h(a) {
                return a.map(function (a) {
                    return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
                }).join('');
            }
            function a() {
                return String.fromCharCode.apply(null, arguments);
            }
        }
        var b = [];
        console.log(d(c, b)() + 123);
    }
}())

在这种情况下,硬编码字符串正在以一种令人困惑的方式重新计算,因此从阅读代码来看它是什么远非显而易见。

因为示例代码简单仔细看还是能看懂的,上面代码只是把 Hello, World! 翻译成 UTF-16 代码单元序列,然后通过 String.fromCharCode() 方法还原,比如 String.fromCharCode("72") 就是大写的 H,因为序列是数字就可以加入复杂的计算逻辑。

命名重整(Name Mangling)

Mangling 是一种为了优化和混淆目的而缩短变量和属性名称的转换。

考虑以下 JS 代码:

let oneTwoThree = 123;
let hello = "Hello, world! ";
console.log(hello + oneTwoThree);

让我们使用 DefendJS 的修饰功能:

$ cp hello2.js mangling.js 
$ defendjs --input mangling.js --features mangle --output .

结果代码是:

(function () {
    var a = 123;
    var b = 'Hello, world! ';
    console.log(b + a);
}())

oneTwoThree成为a并重hello命名为b. 这并没有使一个微不足道的例子变得更难阅读,而是可以做的另一件事是使大块的 JS 难以理解,同时使包含内联 JS 的 HTML 页面更轻量。开发人员倾向于以表示其含义的方式命名他们的变量,当代码被破坏时,这些含义就会丢失。

代码压缩

这本身并不是混淆技术,但压缩可以从代码中删除缩进、换行符和空格通常作为混淆过程的一部分,与其他技术结合使用。

JSFuck

JSFuck是一种将 JS 代码转换为类似于臭名昭著的 Brainfuck 编程语言的深奥风格的方法。false 成为 ![]true 成为 !![] 等等。

将我们的原始 JS 代码转换为 JSFuck 版本后,我们得到如下结果:

![图片转存失败,建议将图片保存下来直接上传        [][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]][([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]((! +[])[+!+[]]+(! +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+([][[]]+[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+!+[]]+(+[ ]+[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+!+[]]]+(! +[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]][([][[]]+[])[+!+[]]+( +[])[+!+[]]+((+[])[([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(! +[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+( +[])[+!+[]]+( +[])[!+[]+!+[]])()([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]][([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]((! +[])[+!+[]]+(! +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+([][[]]+[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+!+[]]+([]+[])[( +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(! +[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+( +[])[!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+((! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]]+(! +[])[!+[]+!+[]+!+[]]+(+(+!+[]+[+!+[]]+(! +[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+( +[])[!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[+!+[]]+[+[]]+(! +[])[!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]]+( +[])[!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(! +[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(! +[])[+!+[]]+( +[])[!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]]+[+[]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]]+(! +[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]])[( +[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]][([][[]]+[])[+!+[]]+( +[])[+!+[]]+((+[])[([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(! +[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+( +[])[!+[]+!+[]]+([ ]+[][[]])[+!+[]+[+[]]]+(! +[])[+[]]]((! +[])[+[]])[([][(! +[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([ ]+[][[]])[+!+[]+[+[]]]+(! +[])[!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([ ]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]](([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]][([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]((! +[])[+!+[]]+(! +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+([][[]]+[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+!+[]]+( +[+[]])[([ ]+[][[]])[+!+[]+[+[]]]+(! +[])[+[]]+( +[])[+!+[]]+( +[])[!+[]+!+[]]+([ ]+[][[]])[+!+[]+[+[]]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+ +( +[+[]])[([ ]+[][[]])[+!+[]+[+[]]]+(! +[])[+[]]+( +[])[+!+[]]+( +[])[!+[]+!+[]]+([ ]+[][[]])[+!+[]+[+[]]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])()[([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+( +[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[])[+!+[]]+([][[]]+[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]](( +[+[]])[([ ]+[][[]])[+!+[]+[+[]]]+(! +[])[+[]]+( +[])[+!+[]]+( +[])[!+[]+!+[]]+([ ]+[][[]])[+!+[]+[+[]]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+( +[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])+[])[+!+[]])+([]+[])[( +[])[+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(! +[])[+[]]+([][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]]+[])[!+[]+!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+( +[])[!+[]+!+[]]+(! +[][( +[])[+[]]+( +[])[!+[]+!+[]]+( +[])[+!+[]]+(! +[])[+[]]])[+!+[]+[+[]]]+(! +[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])())
]()```

![截图 3](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/124becf6518349929872e70e108afefc~tplv-k3u1fbpfcp-zoom-1.image)

现在,这实际上是不可读的,但是有一种方法可以,通过执行 AST 操作来消除这种混乱。