Усиление безопасности обфускации JavaScript

JavaScript

предисловие

На поле боя атак и защиты внешний код общедоступен, поэтому имеет ли смысл шифровать внешний интерфейс? Вероятно, ответ для большинства людей таков:毫无意义, не создавайте собственный алгоритм шифрования, просто используйте HTTPS. Но на самом деле, даже если вы не разбираетесь в криптографии, вы должны знать, что有意义, так как加密前и解密后ссылка не защищена. HTTPS защищает только транспортный уровень и бесполезен за его пределами.

И ссылка шифрования делится на:

  • Транспортное шифрование (против взлома ссылок)
  • Шифрование данных (анти-взлом протокола)
  • Шифрование кода (сокрытие алгоритмов, защита от отладки...)

В этой статье в основном перечислены некоторые методы шифрования, которые я видел и о которых думал.На самом деле, если быть точным, его следует называть обфускацией, а не шифрованием.

Итак, каков конкретный принцип обфускации кода? На самом деле это очень просто, это убрать из кода как можно больше значимой информации, такой как комментарии, переводы строк, пробелы, знаки минус кода, переименование переменных, переименование свойств (если разрешено), удаление бесполезного кода и т.д. Поскольку код является общедоступным, мы должны признать, что ни один алгоритм не может быть полностью невзламываемым, поэтому мы можем только максимально увеличить стоимость чтения кода злоумышленником.

исходный адрес

Обфускация AST синтаксического дерева

В случае обеспечения исходной функциональности кода мы можем изменить AST кода по мере необходимости, а затем вывести измененный AST для генерации кода для достижения цели обфускации.Наши наиболее часто используемыеuglify-jsВот как код запутывается, конечноuglify-jsОбфускация в основном предназначена для сжатия кода, то есть обфускации имени переменной, о которой мы поговорим ниже.

путаница с именами переменных

Запутать имена переменных в символах, которые трудно прочитать, и усложнить чтение кода, как упоминалось выше.uglify-jsПутаница заключается в том, чтобы запутать переменную в коротком имени (в основном для сжатия кода), и теперь большая часть путаницы в направлении безопасности запутает ее в шестнадцатеричном имени переменной, эффект будет следующим:

var test = 'hello';

После обфускации:

var _0x7deb = 'hello';

Меры предосторожности:

  1. eval, исходное имя переменной может использоваться в функции eval.Если оно не обработано, оно может запуститься и сообщить об ошибке следующим образом:

    var test = 'hello';
    eval('console.log(test)');
    

    Если вы не перепутаете console.log(test) в eval, будет сообщено об ошибке. Однако, если синтаксис eval выходит за рамки статического анализа, например:

    var test = 'hello';
    var variableName = 'test';
    eval('console.log(' + variableName + ')');
    

    Что с этим делать, возможно придется пройтись по AST, чтобы найти его работающий результат, а потом его запутать, но похоже, что стоимость относительно высока.

  2. Кодирование глобальных переменных, если код выводится в виде SDK, нам нужно оставить имя глобальной переменной без изменений, например:

    <script>
    var $ = function(id) {
        return document.getElementById(id);
    };
    </script>
    

    $Переменная помещается под глобальную, после обфускации выглядит следующим образом:

    <script>
    var _0x6482fa = function(id) {
        return document.getElementById(id);
    };
    </script>
    

    Затем, если модуль, который зависит от этого фрагмента кода, используйте$('id')Вызов, естественно, сообщит об ошибке, потому что глобальная переменная была запутана.

Постоянное извлечение

Извлеките константы в JS в массив, и используйте метод индекса массива для вызова при вызове.В этом случае это в принципе невозможно прочитать и понять напрямую.Либо обратная обработка AST, либо пошаговая отладка сильно увеличат нагрузку .

Возьмите приведенный выше код в качестве примера:

var test = 'hello';

После обфускации:

var _0x9d2b = ['hello'];

var _0xb7de = function (_0x4c7513) {
    var _0x96ade5 = _0x9d2b[_0x4c7513];
    return _0x96ade5;
};

var test = _0xb7de(0);

Конечно, мы можем преобразовать массив в двузначный массив, трехмерный массив и т. д. в соответствии с нашими потребностями, и нам нужно только получить его там, где он нам нужен.

Постоянная путаница

Зашифровать константу.В приведенном выше коде, хотя это уже обфусцированный код, ноhelloСтрока по-прежнему появляется в коде в виде открытого текста, а Unicode ключевого слова может быть закодирован в шестнадцатеричном виде с помощью функции, позволяющей напрямую декодировать шестнадцатеричное кодирование в JS. следующее:

var test = 'hello';

В сочетании с постоянным извлечением для получения запутанных результатов:

var _0x9d2b = ['\x68\x65\x6c\x6c\x6f'];

var _0xb7de = function (_0x4c7513) {
    _0x4c7513 = _0x4c7513 - 0x0;
    var _0x96ade5 = _0x9d2b[_0x4c7513];
    return _0x96ade5;
};

var test = _0xb7de('0x0');

Конечно, в дополнение к автоматическому анализу Unicode, который поставляется с функцией JS, вы также можете настроить некоторые алгоритмы шифрования и дешифрования, такие как кодирование base64 для констант или другой rc4 и т. д., и дешифрование в порядке, когда вам нужно только используйте его, например, приведенный выше код.После кодирования с помощью base64:

var _0x9d2b = ['aGVsbG8=']; // base64编码后的字符串

var _0xaf421 = function (_0xab132) {
    // base64解码函数
    var _0x75aed = function(_0x2cf82) {
        // TODO: 解码
    };
    return _0x75aed(_0xab132);
}

var _0xb7de = function (_0x4c7513) {
    _0x4c7513 = _0x4c7513 - 0x0;
    var _0x96ade5 = _0xaf421(_0x9d2b[_0x4c7513]);
    return _0x96ade5;
};

var test = _0xb7de('0x0');

оперативная путаница

Цель превращения всех логических операторов и бинарных операторов в функции — повысить сложность чтения кода, сделав невозможным получение результатов напрямую с помощью статического анализа. следующее:

var i = 1 + 2;
var j = i * 2;
var k = j || i;

После обфускации:

var _0x62fae = {
    _0xeca4f: function(_0x3c412, _0xae362) {
        return _0x3c412 + _0xae362;
    },
    _0xe82ae: function(_0x63aec, _0x678ec) {
        return _0x63aec * _0x678ec;
    },
    _0x2374a: function(_0x32487, _0x3a461) {
        return _0x32487 || _0x3a461;
    }
};

var i = _0x62fae._0e8ca4f(1, 2);
var j = _0x62fae._0xe82ae(i, 2);
var k = _0x62fae._0x2374a(i, j);

Конечно, в дополнение к логическим операторам и бинарным операторам, вызовы функций и статические строки также могут быть перепутаны следующим образом:

var fun1 = function(name) {
    console.log('hello, ' + name);
};
var fun2 = function(name, age) {
    console.log(name + ' is ' + age + ' years old');
}

var name = 'xiao.ming';
fun1(name);
fun2(name, 8);
var _0x62fae = {
    _0xe82ae: function(_0x63aec, _0x678ec) {
        return _0x63aec(_0x678ec);
    },
    _0xeca4f: function(_0x92352, _0x3c412, _0xae362) {
        return _0x92352(_0x3c412, _0xae362)
    },
    _0x2374a: 'xiao.ming',
    _0x5482a: 'hello, ',
    _0x837ce: ' is ',
    _0x3226e: ' years old'
};

var fun1 = function(name) {
    console.log(_0x62fae._0x5482a + name);
};
var fun2 = function(name, age) {
    console.log(name + _0x62fae._0x837ce + age + _0x62fae._0x3226e);
}

var name = _0x62fae._0x2374a;
_0x62fae._0xe82ae(fun1, name);
_0x62fae._0xeca4f(fun2, name, 0x8);

В приведенном выше примере добавление строк в fun1 и fun2 также будет запутано, а статические строки также будут добавлены вышеупомянутым字符串提取Извлеките его в массив (мне просто лень, поэтому я не буду писать эту часть кода).

Следует отметить, что каждый раз, когда мы сталкиваемся с одним и тем же оператором, нам нужно перегенерировать функцию, чтобы заменить его, что основано на личных потребностях.

грамматическое уродство

Спутать синтаксис, который мы обычно используем, с синтаксисом, который мы обычно не используем, при условии, что функция кода не изменится. Например, замените for на do/while следующим образом:

for (i = 0; i < n; i++) { 
    // TODO: do something
}

var i = 0;
do {
    if (i >= n) break;
    
    // TODO: do something
    i++;
} while (true)

динамическое исполнение

Добавьте динамическую оценку к статическому исполняемому коду и динамически определяйте оператор во время выполнения, который мешает статическому анализу.

следующее:

var c = 1 + 2;

После обфускации:

function _0x513fa(_0x534f6, _0x85766) { return _0x534f6 + _0x85766; }
function _0x3f632(_0x534f6, _0x534f6) { return _0x534f6 - _0x534f6; }

// 动态判定函数
function _0x3fa24() {
    return true;
}

var c = _0x3fa24() ? _0x513fa(1, 2) : _0x3f632(1, 2);

Путаница в процессе

Обфускация процесса выполнения, также известная как выравнивание потока управления, зачем нам нужно запутывать процесс выполнения? Потому что в процессе разработки кода, чтобы сделать логику кода понятной и простой в обслуживании и расширении, логика кода будет очень ясной. Часть кода получает разные результаты на входе, через различные ветки if/else и последовательно выполняется, и нам нужно спутать эти процессы выполнения и процессы оценки, чтобы злоумышленникам было не так просто понять нашу логику выполнения.

Сглаживание потока управления делится на последовательное сглаживание и условное сглаживание.

выравнивание заказа

Как следует из названия, код, который выполняется последовательно и сверху вниз, для выполнения разбивается на несколько ветвей, как показано ниже:

(function () {
    console.log(1);
    console.log(2);
    console.log(3);
    console.log(4);
    console.log(5);
})();

Блок-схема выглядит следующим образом:

Код после обфускации выглядит следующим образом:

(function () {
    var flow = '3|4|0|1|2'.split('|'), index = 0;
    while (!![]) {
        switch (flow[index++]) {
        case '0':
            console.log(3);
            continue;
        case '1':
            console.log(4);
            continue;
        case '2':
            console.log(5);
            continue;
        case '3':
            console.log(1);
            continue;
        case '4':
            console.log(2);
            continue;
        }
        break;
    }
}());

Запутанная блок-схема выглядит следующим образом:

процесс выглядит.

условное выравнивание

Функция условного выравнивания состоит в том, чтобы объединить все процессы ветвей if/else в один процесс и иметь одинаковые вход и выход на блок-схеме.

Например, следующий код:

function modexp(y, x, w, n) {
    var R, L;
    var k = 0;
    var s = 1;
    while(k < w) {
        if (x[k] == 1) {
            R = (s * y) % n;
        }
        else {
            R = s;
        }
        s = R * R % n;
        L = R;
        k++;
    }
    return L;
}

Приведенный выше код, блок-схема выглядит так

Код после выравнивания потока управления выглядит следующим образом:

function modexp(y, x, w, n) {
    var R, L, s, k;
    var next = 0;
    for(;;) {
        switch(next) {
        case 0: k = 0; s = 1; next = 1; break;
        case 1: if (k < w) next = 2; else next = 6; break;
        case 2: if (x[k] == 1) next = 3; else next = 4; break;
        case 3: R = (s * y) % n; next = 5; break;
        case 4: R = s; next = 5; break;
        case 5: s = R * R % n; L = R; k++; next = 1; break;
        case 6: return L;
        }
    }
}

Запутанная блок-схема выглядит следующим образом:

Интуитивное ощущение, что код меняетсяТеперь все коды сжаты в один слой.Преимущество этого в том, что он не позволяет злоумышленнику быть интуитивным или судить, какие коды выполняются первыми, а какие выполняются статическим анализом.Порядок выполнения должен быть записан динамическим запуском, таким образом увеличивая нагрузку анализа.

Следует отметить, что в нашем процессе, независимо от того, является ли он последовательным процессом или условным процессом, если есть объявление переменной с блочной областью видимости (const/let), то описанный выше процесс будет сглажен. Для области блока после того, как выражение разделено на случай, другие случаи не могут получить объявление переменной const/let, и, естественно, будет сообщено об ошибке.

непрозрачный предикат

Вышеупомянутое суждение о переключении/деле оценивается в виде чисел (то есть предикатов), и оно прозрачно и его можно увидеть.Чтобы запутать аудиторию, суждение по делу можно задать как выражение, чтобы оно не могло быть непосредственно суждением, например, используя приведенный выше код, изменить на непрозрачный предикат:

function modexp(y, x, w, n) {
    var a = 0, b = 1, c = 2 * b + a;
    var R, L, s, k;
    var next = 0;
    for(;;) {
        switch(next) {
        case (a * b): k = 0; s = 1; next = 1; break;
        case (2 * a + b): if (k < w) next = 2; else next = 6; break;
        case (2 * b - a): if (x[k] == 1) next = 3; else next = 4; break;
        case (3 * a + b + c): R = (s * y) % n; next = 5; break;
        case (2 * b + c): R = s; next = 5; break;
        case (2 * c + b): s = R * R % n; L = R; k++; next = 1; break;
        case (4 * c - 2 * b): return L;
        }
    }
}

Предикат состоит из трех переменных, a, b и c. Эти три переменные могут быть даже скрыты в глобальном определении или в массиве, чтобы злоумышленники не могли легко их найти.

упаковщик скриптов

Закодируйте скрипт, декодируйте его во время выполнения и выполните eval следующим образом:

eval (…………………………..……………. ……………. !@#$%^&* ……………. .…………………………..……………. )

Но на самом деле это не имеет особого смысла, потому что злоумышленнику нужно только выставить алерт или console.log

План улучшения: использоватьFunction / (function(){}).constructorПередайте код в виде строки и выполните его следующим образом:

var code = 'console.log("hellow")';
(new Function(code))();

Приведенный выше код может быть зашифрован и запутан, напримерaaencode, принцип тот же, приводим пример

alert("Hello, JavaScript");

После обфускации с помощью aaencode код выглядит следующим образом:

゚ω゚ノ= /`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 -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

Этот код выглядит странно, в отличие от кода JavaScript, но на самом деле этот код использует некоторые символы, похожие на эмодзи, для объявления 16-битного массива (используемого для представления шестнадцатеричных позиций), а затем обрабатывает код как обход строки, пропуская каждый символ кода черезstring.charCodeAtВозьмите этот 16-битный индекс массива и объедините его в код. Общий смысл состоит в том, чтобы рассматривать код как строку, а затем использовать конкатенацию этих символов для замены этого фрагмента кода (вы можете видеть, что в коде много плюсов), и, наконец, по(new Function(code))('_')воплощать в жизнь.

Внимательно соблюдайте приведенный выше код, поместите код в конец('_')Удалите, запустите, вы увидите исходный код непосредственно, затемFunction.constructorсуществует(゚Д゚)В переменной заинтересованные студенты могут просмотреть ее самостоятельно.

В дополнение к aaencode,jjencodeПринцип тот же, поэтому объяснять не буду, остальные более властныеjsfuck, это все зашифрованные коды, подробно описывать которые здесь не будем.

Анти-отладка

Поскольку JavaScript поставляется сdebuggerсинтаксис, мы можем воспользоваться бесконечным цикломdebugger, когда страница открывает панель отладки, она бесконечно переходит в состояние отладки.

Выполнение по времени

Когда код начнет выполняться, используйтеsetIntervalПериодически запускайте нашу функцию защиты от отладки.

случайное исполнение

На этапе генерации кода наша антиотладочная функция случайным образом внедряется в часть тела функции.Когда код выполняется по определенной логике, если панель отладки открыта, он бесконечно переходит в состояние отладки.

Мониторинг контента

Поскольку наш код мог быть отлажен, злоумышленник может скопировать код в свою локальную область, затем изменить, отладить и выполнить его. В это время необходимо добавить некоторые обнаружения, чтобы принять решение. Если он не выполняется в в нормальной среде код не работает сам по себе.

Самопроверка кода

Когда код генерируется, для функции генерируется хэш, и перед выполнением кода используется метод функции toString, чтобы определить, был ли код подделан.

function module() {
    // 篡改校验
    if (Hash(module.toString()) != 'JkYxnHlxHbqKowiuy') {
        // 代码被篡改!
    }
}

Экологическая самопроверка

Проверьте среду выполнения текущего сценария, например, находится ли текущий URL-адрес в разрешенном белом списке и является ли текущая среда обычным браузером.

Если это среда Nodejs, если есть ненормальная среда, мы можем даже запустить троянского коня и долго его отслеживать.

Внедрение ненужного кода

Вставьте какой-нибудь код, которого никогда не будет, чтобы злоумышленник был сбит с толку этим бесполезным ненужным кодом при анализе кода, что затруднило его чтение.

Инъекция ненужной логики

Противоположностью ненужному коду является полезный код. Эти полезные коды представляют собой логику кода, который должен быть выполнен. В настоящее время мы можем собрать эту логику и добавить суждение, чтобы решить, следует ли выполнять истинную логику или ложную логику, следующим образом:

(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
        };
        var bar = function () {
            console.log('def');
        };
        var baz = function () {
            console.log('ghi');
        };
        var bark = function () {
            console.log('jkl');
        };
        var hawk = function () {
            console.log('mno');
        };
 
        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

Видно, что все console.logs — это наша логика выполнения.В это время мы можем собрать все console.logs, а затем вынести ложные суждения для выполнения реального кода логики.После сбора логики инъекции, она выглядит следующим образом:

(function(){
    if (true) {
        var foo = function () {
            if ('aDas' === 'aDas') {
                console.log('abc');
            } else {
                console.log('def');
            }
        };
        var bar = function () {
            if ('Mfoi' !== 'daGs') {
                console.log('ghi');
            } else {
                console.log('def');
            }
        };
        var baz = function () {
            if ('yuHo' === 'yuHo') {
                console.log('ghi');
            } else {
                console.log('abc');
            }
        };
        var bark = function () {
            if ('qu2o' === 'qu2o') {
                console.log('jkl');
            } else {
                console.log('mno');
            }
        };
        var hawk = function () {
            if ('qCuo' !== 'qcuo') {
                console.log('jkl');
            } else {
                console.log('mno');
            }
        };
 
        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

Некоторые строки генерируются в логике принятия решений.При отсутствии извлечения строк реальную логику выполнения можно получить посредством статического анализа кода, или мы можем использовать упомянутое выше динамическое выполнение для определения выполнения реальной логики, вы можете см. эффект использования извлечения строки и кодирования имени переменной следующим образом:

var _0x6f5a = [
    'abc',
    'def',
    'caela',
    'hmexe',
    'ghi',
    'aaeem',
    'maxex',
    'mno',
    'jkl',
    'ladel',
    'xchem',
    'axdci',
    'acaeh',
    'log'
];
(function (_0x22c909, _0x4b3429) {
    var _0x1d4bab = function (_0x2e4228) {
        while (--_0x2e4228) {
            _0x22c909['push'](_0x22c909['shift']());
        }
    };
    _0x1d4bab(++_0x4b3429);
}(_0x6f5a, 0x13f));
var _0x2386 = function (_0x5db522, _0x143eaa) {
    _0x5db522 = _0x5db522 - 0x0;
    var _0x50b579 = _0x6f5a[_0x5db522];
    return _0x50b579;
};
(function () {
    if (!![]) {
        var _0x38d12d = function () {
            if (_0x2386('0x0') !== _0x2386('0x1')) {
                console[_0x2386('0x2')](_0x2386('0x3'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x4'));
            }
        };
        var _0x128337 = function () {
            if (_0x2386('0x5') !== _0x2386('0x6')) {
                console[_0x2386('0x2')](_0x2386('0x4'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x7'));
            }
        };
        var _0x55d92e = function () {
            if (_0x2386('0x8') !== _0x2386('0x8')) {
                console[_0x2386('0x2')](_0x2386('0x3'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x7'));
            }
        };
        var _0x3402dc = function () {
            if (_0x2386('0x9') !== _0x2386('0x9')) {
                console[_0x2386('0x2')](_0x2386('0xa'));
            } else {
                console[_0x2386('0x2')](_0x2386('0xb'));
            }
        };
        var _0x28cfaa = function () {
            if (_0x2386('0xc') === _0x2386('0xd')) {
                console[_0x2386('0x2')](_0x2386('0xb'));
            } else {
                console[_0x2386('0x2')](_0x2386('0xa'));
            }
        };
        _0x38d12d();
        _0x128337();
        _0x55d92e();
        _0x3402dc();
        _0x28cfaa();
    }
}());

Ловушка оценки

В дополнение к внедрению логики выполнения, скрытая ловушка также может быть зарыта в永不到达и无法静态分析В ветке , обратитесь к этой функции, обычные пользователи не будут ее выполнять, а при обходе и оценке AST сработает ловушка! Что может ловушка?

  • Ведите отчет, чтобы быть в курсе ситуации
  • Храните стеганографические функции локально для долгосрочного отслеживания
  • Освободите уязвимость CSRF, получите информацию о взломщике
  • Запустить программу самоубийства (крах страницы, бесконечный цикл, исчерпание памяти и т. д.)

Пакерная помеха

Оберните код с помощью eval, затем зашифруйте параметр eval и закопайте ловушку, вставьте бесполезный код во время декодирования, помешайте отображению и большое количество специальных символов, таких как разрывы строк, комментарии и строки, вызывающие зависание дисплея. .

Заканчивать

Вероятно, путаница, о которой я думаю, включает в себя это. Если используется одна функция, эффект путаницы средний. Если каждая функция используется в комбинации, конечный эффект очевиден. Конечно, это зависит от индивидуальных потребностей. В конце концов, путаница это палка о двух концах, которая одновременно увеличивает сложность чтения, но также увеличивает размер скрипта и снижает эффективность кода.

использованная литература

Обфускация кода — выравнивание потока управления и теория непрозрачных предикатов