на основе Babel верно JS Веб-сайт, который запутывает и восстанавливает код JS Обфускация и восстановление кода (kuizuo.cn)
AST Это всего лишь статический анализ, но вы можете заменить исходный код кодом с сайта, чтобы лучше динамически находить соответствующие точки. К слову, не весь код может с первого взгляда увидеть логику выполнения кода. Он не всемогущ, если у вас есть сильный. js Обратная возможность, иногда динамическая отладка даже лучше, чем AST Статический анализ дает удвоенный результат при вдвое меньших усилиях.
Идентификаторы могут быть определены по желанию. Пока переменные не конфликтуют, я могу определить их по своему желанию. Тогда было решено, что мы не можем восстановить имена переменных исходного кода, поэтому мы можем восстановить только некоторые причудливые инструкции. код выглядит лучше и облегчает отладку.
Есть много способов запутать,Есть много способов соответствовать верному снижению.,Сбивающее с толку снижение стоимости, приведенное выше, может относиться только к запутанному набору кодов «верно».,Если вы возьмете еще один запутанный код,Затем выполните эту программу,Программа, скорее всего, сообщит об ошибке. Так что всемогущего кодкода нет абсолютно,Все программы снижения цен,Нужны иглыверно Для выполнения используются различные методы обфускации.иметь дело с.
Я просто интегрировал методы обфускации, с которыми столкнулся, в набор кода, не все методы обфускации можно восстановить.
В то же время не стоит слишком усердствовать с восстановлением, поскольку восстановление может легко разрушить исходный код и привести к появлению неизвестных ошибок.
намекать
Если вам нужна настройка,Вы также можете связаться с нами. (Мне еще придется это сказать,Совершенно верно, не может удешевить самый оригинальный код)
Далее основное внимание будет уделено некоторым распространённым методам путаницы (по крайней мере, тем, которые относительно хороши среди тех путаниц, с которыми я столкнулся), и для справки будет приложен верный код (источник кода не будет включен).
Далее я покажу, как восстановить запутанный код. Этот пример — мой первый контакт с обфускацией. Можно сказать, что это самое скользкое восстановление, которое я когда-либо делал. В любом случае, я бросал его 4 или 5 раз.
Вставьте код git адрес js-de-obfuscator/example/deobfuscator/cx
Примечание: js ФайлпроходитьинструментJavaScript Obfuscator Toolзапутыватьиметь дело с.
Сначала вы должны разобрать запутанный код на AST древовидная структура,Это справедливо для любого запутанного звука. Прежде всего, взгляните на код,Найти такие не сложнокод Все в'\x6a\x4b\x71\x4b'
Шестнадцатеричные символы, подобные этому,Можно использовать готовые инструменты,Форматирование ограничит результаты перед кодированием.,Но здесь мы используем ast-действия
проходить AST Проверять node узел,можно найтиvalue
Именно те данные, которые нам нужны,Но то, что здесь показано,extra.raw
,Фактически, вам нужно всего лишь перейти к соответствующему узлу,Затем extra Просто удалите атрибут, то же самое Unicode Кодировка также отображается, как указано выше.
Конкретный код обхода выглядит следующим образом
// Преобразование всех шестнадцатеричных кодировок и кодировок Unicode в обычные символы.
hexUnicodeToString() {
traverse(this.ast, {
StringLiteral(path) {
var curNode = path.node;
delete curNode.extra;
},
NumericLiteral(path) {
var curNode = path.node;
delete curNode.extra;
}
})
}
Затем после обхода замените обработанный код на demo.js, чтобы облегчить последующий процесс восстановления. Однако после обработки остается еще большинство неизвестных строк, которые необходимо расшифровать, и, конечно, есть некоторые необработанные коды.
Если вы попытались статически проанализировать код, вы обнаружите, что некоторые параметры имеют вид call_0x3028, вот так
_0x3028['nfkbEK']
_0x3028('0x0', 'jKqK')
_0x3028('0x1', ')bls')
А если серьезно Проверять Вы найдете выражения членов, такие какMemberExpression
заявление_0x3028["nfkbEK"]
,носуществовать Статья 3заявление Но определите функцию_0x3028
。На самом деле это js Функции, например, следующий код может добавить к функции настраиваемый атрибут.
let add = function (a, b) {
add['abc'] = 123
return a + b
}
console.log(add(1, 2))
console.log(add['abc'])
// 3
// 123
Но это не влияет,Просто упоминание здесь,Не совсемкодвопрос。ив_0x3028
Это функция расшифровки,И траверс_0x3028
выражение вызова,CallExpression с двумя параметрами.
Тогда следующий шаг — сосредоточиться на первых трех предложениях «Проверять», потому что эти три предложения являются ключом к этой путанице.
demo.js
var _0x34ba = ["JcOFw4ITY8KX", "EHrDoHNfwrDCosO6Rkw=",...]
(function(_0x2684bf, _0x5d23f1) {
// Здесь мы просто определяем функцию, которая переупорядочивает массив, но вызов выполняется позже.
var _0x20d0a1 = function(_0x17cf70) {
while (--_0x17cf70) {
_0x2684bf['push'](_0x2684bf['shift']());
}
};
var _0x1b4e1d = function() {
var _0x3dfe79 = {
'data': {
'key': 'cookie',
'value': 'timeout'
},
"setCookie": function (_0x41fad3, _0x155a1e, _0x2003ae, _0x48bb02) {
...
},
"removeCookie": function () {
return "dev";
},
"getCookie": function (_0x23cc41, _0x5ea286) {
_0x23cc41 = _0x23cc41 || function (_0x20a5ee) {
return _0x20a5ee;
};
// Здесь определен вызов функции инструкции цветка для вызова
var _0x267892 = function (_0x51e60d, _0x57f223) {
_0x51e60d(++_0x57f223);
};
// где фактически был сделан звонок
_0x267892(_0x20d0a1, _0x5d23f1);
return _0x1c1cc3 ? decodeURIComponent(_0x1c1cc3[1]) : undefined;
}
}
};
};
_0x1b4e1d();
}(_0x34ba, 296));
var _0x3028 = function (_0x2308a4, _0x573528) {
_0x2308a4 = _0x2308a4 - 0;
var _0x29a1e7 = _0x34ba[_0x2308a4];
// Опустить сто строк кода...
return _0x29a1e7;
};
Нет необходимости внимательно читать опущенный код, поскольку в него будут записаны только эти три оператора. node В памяти (оценка),Затемпозвонить。Следующийанализироватькаждыйзаявление Для чего они все?。
Базовый 99%путаницаПервый оператор представляет собой большой массив,Храните все зашифрованные строки,И что нам нужно сделать, это найти все зашифрованные строки,Сделайте это дешевле.
ЗатемзатемВторой оператор обычно представляет собой самовызывающую функцию.,Принятие больших массивов и перетасованных величин массивов в качестве параметров,Функция в — перетасовать массив.,Это место, выделенное в коде выше.,Но здесь мы просто определяем функцию_0x20d0a1
,игде фактически был сделан звонок _0x1b4e1d
середина_0x3dfe79
.getCookie
серединаназывается,В приведенном выше коде есть комментарии. Если вы идете нормально по шагам, то не обязательно, что анализировать получится.,Это запутанное отвратительное место.
Не путайте,Короче говоря, если вы знаете, что второй оператор используется для изменения порядка массива.,И как бы оно ни было запутано,,мы все можемпроходить eval Давайте вызовем его один раз, подробности см. в коде ниже.
Третье утверждение — это функция шифрования,Фактически, это индекс большого массива, переданного в,Затем верните соответствующие члены массива верно,Просто инкапсулируйте это в функцию здесь,эквивалент оригинала _0x34ba[0]
становиться_0x3028("0x0", "jKqK")
form для получения исходной строки (это всего лишь пример, также задействован фактический второй параметр).
Сказав так много выше, на самом деле нет необходимости понимать конкретную логику путаницы. Нет необходимости понимать, что делает второй переданный параметр, потому что наша конечная цель — это сделать. _0x3028("0x0", "jKqK")
конвертировать в оригиналнить,Затемзаменить текущийузелиз。Все, что вам нужно сделать, это перейти к_0x3028("0x0", "jKqK")
,ЗатемВыполните функцию дешифрования один разПолучите расшифрованный результат,Тогда просто замените его. Поэтому ключевым моментом является то, как выполнить функцию дешифрования.
Сначала запустите три оператора один раз, js В коде, который хочет запустить строку во время выполнения, вы можете использовать оценю, но eval Существует проблема с областью действия, eval Область выполняемого кода является локальной. Если он выходит за пределы текущей области, eval. Выполнение кода эквивалентно его недействительности.,Все доступноwindow.eval
илиglobal.eval
,Запишите это в глобальную область видимости,Поскольку это среда узла,Удобно использоватьglobal.eval
。
Перехватите первые три оператора и используйте eval, чтобы записать их в память.
// Получите узел, где находится функция дешифрования
let stringDecryptFuncAst = this.ast.program.body[2]
// Получить имя функции дешифрования Это _0x3028
let DecryptFuncName = stringDecryptFuncAst.declarations[0].id.name
let newAst = parser.parse('')
newAst.program.body.push(this.ast.program.body[0])
newAst.program.body.push(this.ast.program.body[1])
newAst.program.body.push(stringDecryptFuncAst)
// Преобразуйте эти три части кода в строки. Из-за обнаружения форматирования вам необходимо указать параметры сжатия кода.
let stringDecryptFunc = generator(newAst, { compact: true }).code
// Выполните строку кода формы, чтобы ее можно было nodejs Запустите функцию дешифрования в
global.eval(stringDecryptFunc)
В это время,ты можешь использовать_0x3028("0x0", "jKqK")
вывести расшифрованный результат,Однако вводить каждый вручную все равно слишком хлопотно.,Это можно найти_0x3028
везде называется,Затем определите, является ли это выражением вызова CallExpression.,Затемиспользоватьeval('_0x3028("0x0", "jKqK")')
Получите результат расшифровки. Вот пример обхода.
traverse(this.ast, {
VariableDeclarator(path) {
// Когда имя переменной совпадает с именем функции дешифрования
if (path.node.id.name == DecryptFuncName) {
let binding = path.scope.getBinding(DecryptFuncName)
// ИспользоватьreferencePaths может получить все места, на которые имеются ссылки.
binding &&
binding.referencePaths.map((p) => {
// Определите, является ли родительский узел вызывающим выражением, а параметров — два.
if (p.parentPath.isCallExpression()) {
// Выходные параметры и расшифрованные результаты
let args = p.parentPath.node.arguments.map((a) => a.value).join(' ')
let str = eval(p.parentPath.toString())
console.log(args, str)
p.parentPath.replaceWith(t.stringLiteral(str))
}
})
}
},
})
Упомяните об этом, когда возникла привязка путаницы, которая может получить область действия текущей переменной.,иbinding.referencePaths
Вы можете получить все места для звонков,Тогда вам нужно только определить, является ли это выражением вызова,А когда параметров два,Затем etvval выполняет весь узел,То естьeval('_0x3028("0x0", "jKqK")')
,Затемпроходить replaceWith,Просто замените узел. Передаваемые параметры и зашифрованные результаты примерно отображаются следующим образом:,Можно сделать это самомубегатьпроцедура одного проходасерединаdecStringArr()
0x0 jKqK PdAlB
0x1 )bls jtvLV
0x2 M10H SjQMk
0x3 2Q@E length
0x4 [YLR length
0x5 QvlS charCodeAt
0x6 YvHw IrwYd
0x7 iLkl ClOby
0x8 DSlT console
...
Сравнение (части) кодирования между исходным кодом и имеющимся кодом
var _0x505b30 = (function () {
if (_0x3028('0x0', 'jKqK') !== _0x3028('0x1', ')bls')) {
var _0x104ede = !![]
return function (_0x3d32a2, _0x35fd15) {
if ('bKNqX' === _0x3028('0x2', 'M10H')) {
var _0x46992c,
_0x1efd4e = 0,
_0x5cae2b = d(f)
if (0 === _0xb2c58f[_0x3028('0x3', '2Q@E')]) return _0x1efd4e
for (_0x46992c = 0; _0x46992c < _0xb2c58f[_0x3028('0x4', '[YLR')]; _0x46992c++)
(_0x1efd4e = (_0x1efd4e << (_0x5cae2b ? 5 : 16)) - _0x1efd4e + _0xb2c58f[_0x3028('0x5', 'QvlS')](_0x46992c)), (_0x1efd4e = _0x5cae2b ? _0x1efd4e : ~_0x1efd4e)
return 2147483647 & _0x1efd4e
} else {
var _0x45a8ce = _0x104ede
? function () {
if (_0x3028('0x6', 'YvHw') === _0x3028('0x7', 'iLkl')) {
that[_0x3028('0x8', 'DSlT')]['log'] = func
that[_0x3028('0x9', 'YW6h')][_0x3028('0xa', '&12i')] = func
that[_0x3028('0xb', '1jb4')]['debug'] = func
that[_0x3028('0xc', 'k9U[')][_0x3028('0xd', 'nUsA')] = func
that[_0x3028('0xe', ')bls')][_0x3028('0xf', 'PZDB')] = func
that['console'][_0x3028('0x10', 'r8Qx')] = func
that[_0x3028('0x11', 'AIMj')][_0x3028('0x12', '[YLR')] = func
} else {
if (_0x35fd15) {
if (_0x3028('0x13', 'r8Qx') !== _0x3028('0x14', 'YLF%')) {
var _0x1fa1e3 = _0x35fd15[_0x3028('0x15', 'sLdn')](_0x3d32a2, arguments)
_0x35fd15 = null
return _0x1fa1e3
} else {
_0x142a1e()
}
}
}
}
: function () {}
_0x104ede = ![]
return _0x45a8ce
}
}
} else {
;(function () {
return ![]
}
[_0x3028('0x16', 'Yp5j')](_0x3028('0x17', ']R4I') + _0x3028('0x18', 'M10H'))
[_0x3028('0x19', '%#u0')]('stateObject'))
}
})()
var _0x505b30 = (function () {
if ('PdAlB' !== 'jtvLV') {
var _0x104ede = !![]
return function (_0x3d32a2, _0x35fd15) {
if ('bKNqX' === 'SjQMk') {
var _0x46992c,
_0x1efd4e = 0,
_0x5cae2b = d(f)
if (0 === _0xb2c58f['length']) return _0x1efd4e
for (_0x46992c = 0; _0x46992c < _0xb2c58f['length']; _0x46992c++)
(_0x1efd4e = (_0x1efd4e << (_0x5cae2b ? 5 : 16)) - _0x1efd4e + _0xb2c58f['charCodeAt'](_0x46992c)), (_0x1efd4e = _0x5cae2b ? _0x1efd4e : ~_0x1efd4e)
return 2147483647 & _0x1efd4e
} else {
var _0x45a8ce = _0x104ede
? function () {
if ('IrwYd' === 'ClOby') {
that['console']['log'] = func
that['console']['warn'] = func
that['console']['debug'] = func
that['console']['info'] = func
that['console']['error'] = func
that['console']['exception'] = func
that['console']['trace'] = func
} else {
if (_0x35fd15) {
if ('WuEjf' !== 'qpuuN') {
var _0x1fa1e3 = _0x35fd15['apply'](_0x3d32a2, arguments)
_0x35fd15 = null
return _0x1fa1e3
} else {
_0x142a1e()
}
}
}
}
: function () {}
_0x104ede = ![]
return _0x45a8ce
}
}
} else {
;(function () {
return ![]
}
['constructor']('debu' + 'gger')
['apply']('stateObject'))
}
})()
Можно найти иметь дело с Прошедшийкод По крайней мере нет необходимости динамически вызывать расшифрованные результаты,и нравитсяif ("PdAlB" !== "jtvLV")
Этот видзаявление С первого взгляда видно, что это должно быть true,Но после путаницыif (_0x3028("0x0", "jKqK") !== _0x3028("0x1", ")bls"))
Но не могу видеть,Преимущества статического анализа Это AST。
После выполнения расшифровки строки,Фактически большие массивы и функции дешифрования больше не нужны.,Тогда вы можете пройти shift Удалите первые три утверждения.
// Удалить расшифрованный код из исходного кода
this.ast.program.body.shift()
this.ast.program.body.shift()
this.ast.program.body.shift()
Но в целомНе рекомендуется удалять,Потому что после загрузки нам может потребоваться заменить код на запутанный код на веб-сайте.,Затем выполните динамическую отладку и анализируйте,Но если эти три запутанных утверждения удалить,Это может вызвать ошибки выполнения кода. я раньше удалял,Но пока я не наткнулся на сайт. . .
Наконец все дело завершенокодв методе классаdecStringArr
В приведенном выше коде есть такой фрагмент кода
// Когда имя переменной совпадает с именем функции дешифрования
if (path.node.id.name == DecryptFuncName) {
// ...
Среди них здесь DecryptFuncName верно соответствует имени функции дешифрования function_0x3028.,это искусственное определение содержания,Первые три оператора загружаются одновременно.,В случае, если функция дешифрования находится в четвертом операторе,Или когда есть несколько функций дешифрования,Вам нужно изменить код
// Получите узел, где находится функция дешифрования
let stringDecryptFuncAst = this.ast.program.body[2]
// Получить имя функции дешифрования Это _0x3028
let DecryptFuncName = stringDecryptFuncAst.declarations[0].id.name
let newAst = parser.parse('')
newAst.program.body.push(this.ast.program.body[0])
newAst.program.body.push(this.ast.program.body[1])
newAst.program.body.push(stringDecryptFuncAst)
// Преобразуйте эти три части кода в строки. Из-за обнаружения форматирования вам необходимо указать параметры сжатия кода.
let stringDecryptFunc = generator(newAst, { compact: true }).code
Когда я случайно просмотрел код,Вспышка вдохновения,Функция дешифрования вызывается так часто,Я просто прохожу все функции,и обратитесь к нимreferencePaths
Сортировать от большего к меньшему,Разве вы не знаете, что это функция дешифрования?,Итак, естьfindDecFunctionArr
метод
Вообще говоря,Функции дешифрования обычно определяются после больших массивов и перетасовки массивов.,в приведенном выше коде,Видно, что этопроходить Создайте нижний индекс для поиска Функция расшифровки this.ast.program.body[2];
,Итак, пока вы можете перехватить этот код.
/**
* Найдите функцию дешифрования по количеству вызовов функций.
*/
findDecFunction() {
let decFunctionArr = [];
let index = 0; // Определите индекс оператора, в котором находится функция дешифрования.
// Сначала просмотрите все функции (область действия в программе) и определите, является ли это функцией дешифрования на основе количества ссылок.
traverse(this.ast, {
Program(p) {
p.traverse({
'FunctionDeclaration|VariableDeclarator'(path) {
if (!(t.isFunctionDeclaration(path.node) || t.isFunctionExpression(path.node.init))) {
return;
}
let name = path.node.id.name;
let binding = path.scope.getBinding(name);
if (!binding) return;
// Если она вызывается более 100 раз, вероятно, это функция дешифрования. О конкретной ситуации можно судить на основе фактической ситуации.
if (binding.referencePaths.length > 100) {
decFunctionArr.push(name);
// По последним данным Функция расшифровки Приходить Определите индекс оператора, в котором находится функция дешифрования.
let binding = p.scope.getBinding(name);
if (!binding) return;
let parent = binding.path.findParent((_p) => _p.isFunctionDeclaration() || _p.isVariableDeclaration());
if (!parent) return;
let body = p.scope.block.body;
for (let i = 0; i < body.length; i++) {
const node = body[i];
if (node.start == parent.node.start) {
index = i + 1;
break;
}
}
// После прохождения текущего узла он больше не будет пересекать дочерние узлы.
path.skip();
}
},
});
},
});
let newAst = parser.parse('');
// Вставьте несколько операторов перед функцией дешифрования.
newAst.program.body = this.ast.program.body.slice(0, index);
// Преобразуйте эту часть кода в строку. Поскольку возможно обнаружение форматирования, вам необходимо указать параметры сжатия кода.
let code = generator(newAst, { compact: true }).code;
// Выполните строку кода формы, чтобы ее можно было nodejs Запустите функцию дешифрования в
global.eval(code);
this.decFunctionArr = decFunctionArr;
}
В то же время добавляется атрибут decFunctionArr для представления массива функции дешифрования для использования decStringArr, что устраняет необходимость определения функции дешифрования.
Вот и все,Код Базовый после снижения может дать приблизительную схему статического анализа.,Следующим шагом будет тонкая оптимизация этого коднижения.
Противоположность путанице, правда, свойства слона,Но это не обязательно,Просто это выглядит лучше, чем есть на самом деле.,Влияние не большое. Подробности следующие
changeObjectAccessMode() {
traverse(this.ast, {
MemberExpression(path) {
if (t.isStringLiteral(path.node.property)) {
let name = path.node.property.value
path.node.property = t.identifier(name)
path.node.computed = false
}
}
})
}
существоватьснижениепозжекод Все еще существуютсуществовать!![]
и ![]
или Тот, кто!0
и!1
,иэтотверноотвечать js середина То естьtrue
иfalse
,Таким образом, вы также можете пройти эту часть кода,Затем Воля其снижениестановиться Boolean, я не буду вдаваться в подробности подобных выражений (несколько похожих на jsfuck),ast Саму структуру анализировать. Подробности следующие
traverseUnaryExpression() {
traverse(this.ast, {
UnaryExpression(path) {
if (path.node.operator !== '!') return // избегайте осуждения void
// Определите, является ли второй символ!
if (t.isUnaryExpression(path.node.argument)) {
if (t.isArrayExpression(path.node.argument.argument)) { // !![]
if (path.node.argument.argument.elements.length == 0) {
path.replaceWith(t.booleanLiteral(true))
path.skip()
}
}
} else if (t.isArrayExpression(path.node.argument)) { // ![]
if (path.node.argument.elements.length == 0) {
path.replaceWith(t.booleanLiteral(false))
path.skip()
}
} else if (t.isNumericLiteral(path.node.argument)) { // !0 or !1
if (path.node.argument.value === 0) {
path.replaceWith(t.booleanLiteral(true))
} else if (path.node.argument.value === 1) {
path.replaceWith(t.booleanLiteral(false))
}
} else {
}
}
})
}
снижениепозжекодсередина Все еще существуютсуществовать["constructor"]("debu" + "gger")["call"]("action");
этот样иззаявление,вdebugger
Умышленно разделить на две части, и это тоже можно сделать ast Чтобы восстановить его до полной строки, это также аналогично 1 + 2
Этот буквальный Все можно объединить. Код программы восстановления следующий
traverseLiteral() {
traverse(this.ast, {
BinaryExpression(path) {
let { left, right } = path.node
// Определите, являются ли левая и правая части литералами
if (t.isLiteral(left) && t.isLiteral(right)) {
let { confident, value } = path.evaluate() // Вычислить значение бинома
confident && path.replaceWith(t.valueToNode(value))
path.skip()
}
}
});
}
Среди них уверенно указывает, является ли она вычисляемой, например, переменная + 1. Поскольку в данный момент программа не знает значения переменной, ее невозможно вычислить, а уверенно имеет значение false.
В то же время этот вычислительный биномиальный литерал можно спутать с некоторыми относительно простыми вещами, такими как числовая путаница XOR. 706526 ^ 706516
Рассчитано как 10 Вы можете напрямую заменить исходный узел. Следовательно, этот шаг обхода необходимо выполнить раньше, чем другие обходы.
Некоторым переменным можно назначить один раз, а затем больше не изменять, как и константам, например, в следующем коде.
let a = 100
console.log(a)
Тогда его можно будет полностью заменитьстановитьсяconsole.log(100)
, окончательный эффект на выходе тот же, но предпосылка a Он был назначен только один раз, что также можно сказать a Это должна быть переменная, иначе снижение может привести к сбою исходного результата выполнения и binding можно проверить переменную a история заданий.
traverseStrNumValue() {
traverse(this.ast, {
'AssignmentExpression|VariableDeclarator'(path) {
let _name = null;
let _initValue = null;
if (path.isAssignmentExpression()) {
_name = path.node.left.name;
_initValue = path.node.right;
} else {
_name = path.node.id.name;
_initValue = path.node.init;
}
if (t.isStringLiteral(_initValue) || t.isNumericLiteral(_initValue)) {
let binding = path.scope.getBinding(_name);
if (binding && binding.constant && binding.constantViolations.length == 0) {
for (let i = 0; i < binding.referencePaths.length; i++) {
binding.referencePaths[i].replaceWith(_initValue);
}
}
}
},
});
}
Как упоминалось выше, некоторые замены строк и числовых констант,Игла — это переменная, которая была назначена только один раз.,Но также могут быть случаи, когда переменные не используются.,столкнулся с такой ситуацией,мы можем судить constantViolations элемент пуст, затем удалите его.
removeUnusedValue() {
traverse(this.ast, {
VariableDeclarator(path) {
const { id, init } = path.node;
if (!(t.isLiteral(init) || t.isObjectExpression(init))) return;
const binding = path.scope.getBinding(id.name);
if (!binding || binding.constantViolations.length > 0) return
if (binding.referencePaths.length > 0) return
path.remove();
},
FunctionDeclaration(path){
const binding = path.scope.getBinding(path.node.id.name);
if (!binding || binding.constantViolations.length > 0) return
if (binding.referencePaths.length > 0) return
path.remove();
}
});
}
Есть также несколько бесполезных блоков кода, таких как
function test() {
if (true) {
return '123'
} else {
return Math.floor(10 * Math.random())
}
}
test()
Второй оператор никогда не будет выполнен.,Тогда его можно будет удалить. Хотя редактор кода пометит его как темный,Указывает, что оно не будет выполнено,Но в этой путанице мне бы хотелось иметь меньше кода,Все еще необходимопроходить AST Выполнять операции.
removeUnusedBlockStatement() {
traverse(this.ast, {
IfStatement(path) {
if (t.isBooleanLiteral(path.node.test)) {
let testValue = path.node.test.value
if (testValue === true) {
path.replaceInline(path.node.consequent)
} else if (testValue === false) {
path.replaceInline(path.node.alternate)
}
}
},
});
}
Хотя говорят, что только такая игла if Условие Boolean,если Условиеif(1===1)
Также возможна ситуация,Потому что в предыдущем вычислении снижения биномиального литерала,ужеif(1===1)
заменен на if(true)
,Так что тут нам остаётся только судитьisBooleanLiteral
Вот и все。финальныйснижениепозже结果会Воля if Блоки кода удалены с сохранением BlockStatement, код следующий
function test() {
{
return "123";
}
}
test();
Некоторые коды клавиш будут скрыты в отладчике, setTimeout, setInterval и т. д. При отладке вам необходимо обратить особое внимание на наличие кодов клавиш, поэтому вы можете добавить комментарий в это время, чтобы добавить метку, например TOLOOK, для позиционирования. В частности, он позиционируется в соответствии с указанным идентификатором. Следующий код используется в качестве демонстрации, и в этих местах будут добавлены комментарии // TOLOOK.
addComments() {
traverse(this.ast, {
DebuggerStatement(path) {
path.addComment('leading', ' TOLOOK', true);
},
CallExpression(path) {
if (!['setTimeout', 'setInterval'].includes(path.node.callee.name)) return;
path.addComment('leading', ' TOLOOK', true);
},
StringLiteral(path) {
if (path.node.value === 'debugger') {
path.addComment('leading', ' TOLOOK', true);
}
}
});
}
Этот метод был вызван в начале восстановления, но я должен сказать это здесь еще раз, потому что этот набор запутанных шестнадцатеричных чисел является окончательной обработкой, а это означает, что для нас не проблема использовать восстановление непосредственно в начале, но если он зашифрован. В строке присутствуют символы шестнадцатеричной кодировки, и если этот шаг выполнить до расшифровки строки, то некоторые строки все равно могут отображаться в шестнадцатеричном виде, поэтому этот метод намеренно помещен далее в статье, и этот метод. также можно назвать последним.
hexUnicodeToString() {
traverse(this.ast, {
StringLiteral(path) {
var curNode = path.node;
delete curNode.extra;
},
NumericLiteral(path) {
var curNode = path.node;
delete curNode.extra;
},
});
}
Большинство запутанных идентификаторов похожи на _0x123456, но некоторые из них сильно отличаются, например, ОООО0о. По сравнению с предыдущими код легче увидеть и неправильно прочитать. Тогда вы сможете единожды переименовать все идентификаторы.
renameIdentifier() {
let code = this.code
let newAst = parser.parse(code);
traverse(newAst, {
'Program|FunctionExpression|FunctionDeclaration'(path) {
path.traverse({
Identifier(p) {
path.scope.rename(p.node.name, path.scope.generateUidIdentifier('_0xabc').name);
}
})
}
});
this.ast = newAst;
}
Но узнать идентификатор исходного кода просто невозможно, поэтому невозможно понять код семантически.
Однако есть некоторые конкретные замены, например, для i
for (var _0x1e5665 = 0, _0x3620b9 = this['JIyEgF']['length']; _0x1e5665 < _0x3620b9; _0x1e5665++) {
this['JIyEgF']['push'](Math['round'](Math['random']()))
_0x3620b9 = this['JIyEgF']['length']
}
Нравится этот код,Это вполне возможно_0x1e5665
заменятьстановитьсяi
,Однако общий эффект чтения «верно» невелик.
Есть также некоторые методы восстановления, которые я не буду вдаваться в подробности (в примере здесь не используются), например
Подождите, короче, оптимизировать можно все, что угодно, но восстановленный код может быть непонятен. По сравнению с расшифровкой строк, если вы не можете справиться с расшифровкой строк, то все это напрасно.
Конкретные примеры можно найти в руководстве Пример исходного кода середина Проверятьверно AST операция.
финальныйвесьснижениепозжекод МожетсуществоватьnewCode.js
середина Проверять,Но пока не проверялось, сможет ли код после снижения работать нормально.,Или замена узлов вызывает синтаксические ошибки,Все, что должно бытьснижениепозжекоди Запутать Прошедшийкодзаменятьбегатьэтот样才能测试из出Приходить。Я не буду здесь вдаваться в конкретный процесс выполнения.(因为真из懒得существоватьиметь дело это js файл. . . )
Вышеупомянутая операция снижения для верно на самом деле недостаточно очевидна, поэтому я написал онлайн-верно. JS Обфускация и восстановление сайт кода (в основном для правильного снижения) – JS Обфускация и восстановление кода (kuizuo.cn)
Фактически, вышеупомянутый снижениекод упакован в инструмент для использования.