基础部分

AST:Abstract Syntax Tree(抽象语法树),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

我知道,看完定义的你八成是一脸懵逼,什么叫做源代码语法结构的抽象表示?不用害怕,我在这里简单使用一句话为例来帮助大家理解:

今天你喝水了吗?

大家都知道这句话的意思就是问今天你喝水了吗,但你知道这句话是由哪些成分组成的吗?

以前上学的时候语文课肯定都学过,句子里有主语,谓语,宾语,定语等等,句子中的词又分为名词、动词、介词…各种词,当这些词按照某种规律形式组合以后,就形成了上面的那句话,

而现在正在看文章的你肯定知道并理解上面那句话的意思,但是,你不一定知道这句话中每个词的词性,不知道哪个是主语,哪个是谓语,这就跟你知道 JavaScript,却不了解AST是一回事。

所以:

不会AST这件事本身不会对你学习使用JavaScript有任何影响,但如果你理解并掌握了AST,那么JavaScript 可能在你眼里就有些不太一样了,从某种程度上来说,了解掌握AST可以帮助你真正吃透 JavaScript 的语言精髓。

既然如此,那怎样才能快速的了解AST?

答案很简单,随写写句JS拿去AST分析一下,然后对着看看,相信聪明的你很快就能有所了解。

在线 AST语法结构解析网站:https://astexplorer.net/

下面来看看最最简单的JS

var a=1;

代码对应的AST语法树结构:

从上往下看,有Program、declarations、VariableDeclarator、Identifier、Literal,这些都AST的结构类型,除了这些还有很多,列表如下:

知道了这些有什么用呢?除了上面说的可以加深对JS的深层理解外,最大的用处就是对 JavaScript 代码进行混淆以及还原了。

了解JavaScript 的小伙伴应该都知道,JS有非常非常多的语法糖,而对JS的加密混淆,其实就是在对这些语法糖的充分利用,当你既熟练掌握JavaScript ,又理解并掌握了AST以后,你就可以从AST语法树结构的视角,去对混淆后的JS代码进行某种程度的还原,有点类似于降维打击,因为JS加密混淆都是在JS代码的外在表现形式上做的手脚,而AST的视角则更偏向底层。

混淆JS还原演示

PS:环境问题请自行百度解决。
测试代码:

function MyFun(a,b){var c=a+b;var d=a*b+c;return c+d;
}
console.log(MyFun(10,20))

加密混淆后的代码:

var _0x305a = ['B8OBMcKcUQ==', 'w4Y2wrrCl8OT', 'wpHCpMKLRsOu', 'wpgfwo7DgMO4', 'wpVywozCqwHCucKV', 'HMK1VsOS', 'wrfCiSgfw7c0NGHCoAXCuH7CnyzDlMOdfXxHLDorfsOCwrzCnFNPw6I+cwQ=', 'SsO0PUdW', 'PcKRVQ==', 'UHbDj3xcw7ZY', 'UsO6OA==', 'wrjChmcfw70=', 'W8OnLURB', 'w6/CrATDncKcw6lF', 'IcOxw4DDiMOx', 'wr3Dp07DtUUfwrI=', 'N8KOesOsw58=', 'FR7DgS0p', 'AgPDjj0jcUI=', 'AsOYGMKFdg==', 'bcK0wrHCiMOt', 'w7HClAPDo8KVSsKe', 'wrbDhGR1', 'ABzDkCI1', 'Fg1fRsOE', 'w4XCocOJBGg=', 'w60AwpHCv8OJ', 'NcKLwpBPHW01', 'w4QGIWLDiw==', 'wqXCmmAT', 'FT9dMg==', 'LF7Du2sqwrrCqsKDRw==', 'wr/DsknDs3s=', 'wrXDhHR3BQ==', 'P8KKwphT', 'RHjDk2E=', 'w7FCY21yw7BV', 'w7tDa3E=', 'w77ClAo=', 'AcOWJMKKRsOCwpbDsRLDlHkTw5g/wqfCgDzCusOC', 'w6fDpAHDiRlpWQ==', 'wpgGwo3Dtg==', 'w4c+wqHDh2E=', 'fXjDpUspw5kAH1jDvcOGAMKhfiHCiFvCkcOLGMOqw5HDtsKTw5jDom7DmMOaYMKdwrs=', 'QhFqYQg=', 'wqHCsm3Ds0DCpsKB', 'w4cdwo4=', 'w75Cag==', 'w4YiEcO9wq1aIw==', 'w48XwovCvcOY', 'e1vDnHbCg8Kkdkp5', 'cEHDtUhR', 'asKwwonCjMOw', 'TsOaw4QMGsKswp4=', 'wqrDqULDqk8=', 'w7DCjHcsw4A=', 'w4N6worDksOo', 'WMKywpDDonI=', 'wo0GwqkKwrA=', 'DjRPKMKlwpROa8OCV8KxY8KYWGEuwobDgcO2', 'CcOZw5jDlcOO', 'bsK4wpLClsOT', 'w6YJw5APwoo=', 'w4EoHcO7wqU=', 'w4lHTTnCpcKkw6jCgUc=', 'wpNlwoHCvR7CocKZwoMe', 'w6XCt8O1I1A=', 'w7dff3Fv', 'FMKrQMORw6M=', 'w7fCgw7DtcKKUsKSLyA=', 'DMOyw6vDucO6', 'L1/Dt1nDuMOlwpQ=', 'w5MbLnLDgcKlwps=', 'wrXCvHHDrg==', 'w5EsHcOiwqc=', 'N8KUwo5QCw==', 'w6sdwo3Co8OUw7xB', 'VABoYw==', 'wrF7LhFwwo5O', 'Rj9O']; (function(_0x1f4b85, _0x305a4c) {var _0x796962 = function(_0x4b72d1) {while (--_0x4b72d1) {_0x1f4b85['push'](_0x1f4b85['shift']());}};_0x796962(++_0x305a4c);
} (_0x305a, 0xe7));
var _0x7969 = function(_0x1f4b85, _0x305a4c) {_0x1f4b85 = _0x1f4b85 - 0x0;var _0x796962 = _0x305a[_0x1f4b85];if (_0x7969['UkyMFX'] === undefined) { (function() {var _0x344906;try {var _0x1d345f = Function('return\x20(function()\x20' + '{}.constructor(\x22return\x20this\x22)(\x20)' + ');');_0x344906 = _0x1d345f();} catch(_0x1d3b2a) {_0x344906 = window;}var _0x14f0f1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x344906['atob'] || (_0x344906['atob'] = function(_0x3128ab) {var _0x23f475 = String(_0x3128ab)['replace'](/=+$/, '');var _0x32bdd4 = '';for (var _0x3da026 = 0x0,_0x372bba, _0x104d49, _0x5eb7a8 = 0x0; _0x104d49 = _0x23f475['charAt'](_0x5eb7a8++);~_0x104d49 && (_0x372bba = _0x3da026 % 0x4 ? _0x372bba * 0x40 + _0x104d49: _0x104d49, _0x3da026++%0x4) ? _0x32bdd4 += String['fromCharCode'](0xff & _0x372bba >> ( - 0x2 * _0x3da026 & 0x6)) : 0x0) {_0x104d49 = _0x14f0f1['indexOf'](_0x104d49);}return _0x32bdd4;});} ());var _0x570ba5 = function(_0x257551, _0x1f613d) {var _0x3e9282 = [],_0x434903 = 0x0,_0x33fc11,_0x249601 = '',_0x2f07c3 = '';_0x257551 = atob(_0x257551);for (var _0x464f7e = 0x0,_0x4b3e63 = _0x257551['length']; _0x464f7e < _0x4b3e63; _0x464f7e++) {_0x2f07c3 += '%' + ('00' + _0x257551['charCodeAt'](_0x464f7e)['toString'](0x10))['slice']( - 0x2);}_0x257551 = decodeURIComponent(_0x2f07c3);var _0x94001b;for (_0x94001b = 0x0; _0x94001b < 0x100; _0x94001b++) {_0x3e9282[_0x94001b] = _0x94001b;}for (_0x94001b = 0x0; _0x94001b < 0x100; _0x94001b++) {_0x434903 = (_0x434903 + _0x3e9282[_0x94001b] + _0x1f613d['charCodeAt'](_0x94001b % _0x1f613d['length'])) % 0x100;_0x33fc11 = _0x3e9282[_0x94001b];_0x3e9282[_0x94001b] = _0x3e9282[_0x434903];_0x3e9282[_0x434903] = _0x33fc11;}_0x94001b = 0x0;_0x434903 = 0x0;for (var _0x27a73e = 0x0; _0x27a73e < _0x257551['length']; _0x27a73e++) {_0x94001b = (_0x94001b + 0x1) % 0x100;_0x434903 = (_0x434903 + _0x3e9282[_0x94001b]) % 0x100;_0x33fc11 = _0x3e9282[_0x94001b];_0x3e9282[_0x94001b] = _0x3e9282[_0x434903];_0x3e9282[_0x434903] = _0x33fc11;_0x249601 += String['fromCharCode'](_0x257551['charCodeAt'](_0x27a73e) ^ _0x3e9282[(_0x3e9282[_0x94001b] + _0x3e9282[_0x434903]) % 0x100]);}return _0x249601;};_0x7969['tMJLOX'] = _0x570ba5;_0x7969['vDRtQv'] = {};_0x7969['UkyMFX'] = !![];}var _0x4b72d1 = _0x7969['vDRtQv'][_0x1f4b85];if (_0x4b72d1 === undefined) {if (_0x7969['SqRviR'] === undefined) {_0x7969['SqRviR'] = !![];}_0x796962 = _0x7969['tMJLOX'](_0x796962, _0x305a4c);_0x7969['vDRtQv'][_0x1f4b85] = _0x796962;} else {_0x796962 = _0x4b72d1;}return _0x796962;
};
function _0x556652(_0x4a2332, _0x2634dc) {var _0x94d946 = function() {if (_0x7969('0xb', 'X*5E') !== _0x7969('0xc', 'WhTf')) {if (fn) {var _0x33221e = fn[_0x7969('0x35', 'hsTm')](context, arguments);fn = null;return _0x33221e;}} else {var _0x43616c = !![];return function(_0x550c05, _0x13d9c2) {if (_0x7969('0xa', '9uv1') !== _0x7969('0x1d', 'wdO$')) {var _0x296878 = _0x43616c ?function() {if (_0x7969('0x22', 'NGkH') !== _0x7969('0x40', 't$pJ')) {if (_0x13d9c2) {if (_0x7969('0x21', '9kDW') === _0x7969('0x17', 'k(YQ')) {that[_0x7969('0x16', 'r8p)')] = function(_0x1c2524) {var _0x2972cc = {};_0x2972cc[_0x7969('0x11', 'k3XH')] = _0x1c2524;_0x2972cc[_0x7969('0x1f', 'Cu4J')] = _0x1c2524;_0x2972cc[_0x7969('0x3a', 'R*hj')] = _0x1c2524;_0x2972cc[_0x7969('0x26', 'V6wH')] = _0x1c2524;_0x2972cc[_0x7969('0x41', 'Bt5v')] = _0x1c2524;_0x2972cc[_0x7969('0x4e', 'kamb')] = _0x1c2524;_0x2972cc[_0x7969('0x10', 'a4uQ')] = _0x1c2524;_0x2972cc[_0x7969('0x9', 'uP&h')] = _0x1c2524;return _0x2972cc;} (func);} else {var _0x34a69a = _0x13d9c2[_0x7969('0x20', 'sT$v')](_0x550c05, arguments);_0x13d9c2 = null;return _0x34a69a;}}} else {that = window;}}: function() {};_0x43616c = ![];return _0x296878;} else {var _0x2c3174 = {};_0x2c3174[_0x7969('0x8', 'pE[N')] = func;_0x2c3174[_0x7969('0x32', 'WhTf')] = func;_0x2c3174[_0x7969('0x47', 'sDkG')] = func;_0x2c3174[_0x7969('0x27', 'YgD$')] = func;_0x2c3174[_0x7969('0x23', '9uv1')] = func;_0x2c3174[_0x7969('0x4a', '(^8)')] = func;_0x2c3174[_0x7969('0x2a', 'Cu4J')] = func;_0x2c3174[_0x7969('0x25', 'AWI1')] = func;return _0x2c3174;}};}} ();var _0x23c1f6 = _0x94d946(this,function() {var _0x6e83c1 = function() {};var _0x3f3777;try {if (_0x7969('0x33', 'JKNM') !== _0x7969('0x45', 'k(YQ')) {var _0x584a01 = Function(_0x7969('0x30', 'uP&h') + _0x7969('0xf', 'V6wH') + ');');_0x3f3777 = _0x584a01();} else {var _0x30c130 = Function(_0x7969('0x44', 'YgD$') + _0x7969('0x34', 'Jler') + ');');_0x3f3777 = _0x30c130();}} catch(_0x5365b4) {if (_0x7969('0x19', 'D3T3') !== _0x7969('0x1c', 'uP&h')) {_0x3f3777 = window;} else {_0x3f3777[_0x7969('0x1', 'AWI1')][_0x7969('0x38', 'sJv5')] = _0x6e83c1;_0x3f3777[_0x7969('0x12', 'O(f6')][_0x7969('0x2c', 'O(f6')] = _0x6e83c1;_0x3f3777[_0x7969('0x1e', 'kamb')][_0x7969('0x48', 'aCn%')] = _0x6e83c1;_0x3f3777[_0x7969('0x3e', ']zyO')][_0x7969('0xe', 'D3T3')] = _0x6e83c1;_0x3f3777[_0x7969('0x31', 'jA(H')][_0x7969('0x4c', 'sJv5')] = _0x6e83c1;_0x3f3777[_0x7969('0x24', '*20m')][_0x7969('0x3b', 'G1i2')] = _0x6e83c1;_0x3f3777[_0x7969('0x7', '1MEH')][_0x7969('0x2a', 'Cu4J')] = _0x6e83c1;_0x3f3777[_0x7969('0x0', 'QGrn')][_0x7969('0x14', 'V6wH')] = _0x6e83c1;}}if (!_0x3f3777[_0x7969('0x39', 'aCn%')]) {if (_0x7969('0x4b', 'NGkH') !== _0x7969('0x3c', 'O(f6')) {_0x3f3777[_0x7969('0x36', 'WU*x')] = function(_0x4bdab5) {if (_0x7969('0x29', 'zEsY') === _0x7969('0x46', 'wdO$')) {var _0x5f33bc = {};_0x5f33bc[_0x7969('0x37', 'R*hj')] = _0x4bdab5;_0x5f33bc[_0x7969('0x6', 'hsTm')] = _0x4bdab5;_0x5f33bc[_0x7969('0x43', 'JQ7t')] = _0x4bdab5;_0x5f33bc[_0x7969('0x2e', 'sJv5')] = _0x4bdab5;_0x5f33bc[_0x7969('0x3d', 'wdO$')] = _0x4bdab5;_0x5f33bc[_0x7969('0x49', 'U(TC')] = _0x4bdab5;_0x5f33bc[_0x7969('0x3f', 'zEsY')] = _0x4bdab5;_0x5f33bc[_0x7969('0x1a', 'sT$v')] = _0x4bdab5;return _0x5f33bc;} else {var _0x1b640b = firstCall ?function() {if (fn) {var _0x3dd5c2 = fn[_0x7969('0x4d', 'D3T3')](context, arguments);fn = null;return _0x3dd5c2;}}: function() {};firstCall = ![];return _0x1b640b;}} (_0x6e83c1);} else {var _0x345d2c = fn[_0x7969('0x4', '*20m')](context, arguments);fn = null;return _0x345d2c;}} else {_0x3f3777[_0x7969('0x36', 'WU*x')][_0x7969('0x2f', 'kamb')] = _0x6e83c1;_0x3f3777[_0x7969('0x5', '9uv1')][_0x7969('0x2', 'WU*x')] = _0x6e83c1;_0x3f3777[_0x7969('0x12', 'O(f6')][_0x7969('0x42', 'vJ8P')] = _0x6e83c1;_0x3f3777[_0x7969('0x2d', 'sJv5')][_0x7969('0x2b', '*20m')] = _0x6e83c1;_0x3f3777[_0x7969('0xd', '(^8)')][_0x7969('0x15', 'a4uQ')] = _0x6e83c1;_0x3f3777[_0x7969('0x1b', 'sT$v')][_0x7969('0x28', 'ijQK')] = _0x6e83c1;_0x3f3777[_0x7969('0x1b', 'sT$v')][_0x7969('0x3', 'aCn%')] = _0x6e83c1;_0x3f3777[_0x7969('0x18', 'zEsY')][_0x7969('0x4f', 'k(YQ')] = _0x6e83c1;}});_0x23c1f6();var _0x245e10 = _0x4a2332 + _0x2634dc;var _0x5d196d = _0x4a2332 * _0x2634dc + _0x245e10;return _0x245e10 + _0x5d196d;
}
console[_0x7969('0x13', 'a4uQ')](_0x556652(0xa, 0x14));

检查混淆代码是否可以正常执行:

混淆后的代码可以正常执行,

对混淆后的代码进行初步分析函数 _0x7969 在整个被混淆的JS中大量出现,并且,都是以 _0x7969[‘xxx’] 或 _0x7969(‘xxx’,‘xxx’) 的形式出现,其中的xxx则全部都是乱七八糟的字符串,由此可以得出结论:

_0x7969 这个函数就是这个混淆JS 加密字符串的解密函数

使用支持JS格式的文本编辑器,对混淆后的代码进行收缩,如下:

我给标注出了解密函数跟执行函数(或者叫原始功能函数,叫啥无所谓,明白意思就行)

混淆JS的大体结构我们清楚了,下面第一步,就是对执行函数的加密字符串进行解密

我们先将混沌JS中的解密函数复制出来保存到de.js,将执行函数部分复制出来保存为en.js,再新建一个obTest.js,用于编写AST还原代码

de.js 需要添加一行,导出函数,名称就是上面的 _0x7969,如下:

打开obTest.js,开始进入AST 搬砖环节

const fs = require("fs");
const esprima = require('esprima'); //ECMAScript(JavaScript) 解析架构,主要用于多用途分析。
const estraverse = require('estraverse'); //语法树遍历辅助库(提供了两个静态方法,estraverse.traverse 和 estraverse.replace。前者单纯遍历 AST 的节点,通过返回值控制是否继续遍历到叶子节点;而 replace 方法则可以在遍历的过程中直接修改 AST,实现代码重构功能。)
const escodegen = require('escodegen');//AST的 ECMAScript (也称为JavaScript)代码生成器
const iconv = require("iconv-lite");
const de = require("./de");   //读取加密混淆的执行函数Js
var content = fs.readFileSync('./en.js',{encoding:'binary'});
var buf = new Buffer.from(content,'binary');
var code = iconv.decode(buf,'utf-8');//将混淆后的执行函数Js转换为AST
var ast = esprima.parse(code);
//字符串解密
var ast = esprima.parse(code);
ast = estraverse.replace(ast, {enter: function (node) {if (node.type == 'CallExpression' &&  //标注1node.callee.type == 'Identifier' && //标注2node.callee.name == "_0x7969" &&  //解密函数名node.arguments.length == 2 &&  node.arguments[0].type == 'Literal' && //标注3node.arguments[1].type == 'Literal')  //标注4{var val = de._0x7969(node.arguments[0].value,node.arguments[1].value);  //标注5return {type: esprima.Syntax.Literal,value: val,raw: val}}}
});code = escodegen.generate(ast)  //将AST转换为JS
console.log(code)

上面代码看不懂?没关系,一张图搞定

左边为混淆代码,右边为AST语法树结构,上面我们讲过了,所有加密的字符串都是以 _0x7969(‘xxx’,‘xxx’) 这样的结构出现,所以,我们需要对结构进行筛查判断,找到所有这一类的节点

上面代码中的 if … && …&& 一大串就是干这个事的,拿一行为例说明:

if (node.type == ‘CallExpression’ && //标注1
判断 node.type(当前节点类型)是否为 CallExpression(对应看上面的图),是不是马上就清楚了

执行代码看一下效果(加密字符串解密后)

function _0x556652(_0x4a2332, _0x2634dc) {var _0x94d946 = function () {if ('wxqXe' !== 'wxqXe') {if (fn) {var _0x33221e = fn['apply'](context, arguments);fn = null;return _0x33221e;}} else {var _0x43616c = !![];return function (_0x550c05, _0x13d9c2) {if ('NDYGh' !== 'bvJko') {var _0x296878 = _0x43616c ? function () {   if ('RzWPN' !== 'fJYYY') {if (_0x13d9c2) {if ('CONdw' === 'YqJRn') {      that['console'] = function (_0x1c2524) {var _0x2972cc = {};_0x2972cc['log'] = _0x1c2524;_0x2972cc['warn'] = _0x1c2524;_0x2972cc['debug'] = _0x1c2524;_0x2972cc['info'] = _0x1c2524;_0x2972cc['error'] = _0x1c2524;_0x2972cc['exception'] = _0x1c2524;_0x2972cc['table'] = _0x1c2524;_0x2972cc['trace'] = _0x1c2524;return _0x2972cc;}(func);} else {var _0x34a69a = _0x13d9c2['apply'](_0x550c05, arguments);_0x13d9c2 = null;return _0x34a69a;}}} else {that = window;}} : function () {};_0x43616c = ![];return _0x296878;} else {var _0x2c3174 = {};_0x2c3174['log'] = func;_0x2c3174['warn'] = func;_0x2c3174['debug'] = func;_0x2c3174['info'] = func;_0x2c3174['error'] = func;_0x2c3174['exception'] = func;_0x2c3174['table'] = func;_0x2c3174['trace'] = func;return _0x2c3174;}};}}();var _0x23c1f6 = _0x94d946(this, function () {var _0x6e83c1 = function () {};var _0x3f3777;try {if ('LbvcK' !== 'qYROQ') {var _0x584a01 = Function('return (function() ' + '{}.constructor("return this")( )' + ');');_0x3f3777 = _0x584a01();} else {var _0x30c130 = Function('return (function() ' + '{}.constructor("return this")( )' + ');');_0x3f3777 = _0x30c130();}} catch (_0x5365b4) {if ('BUJQE' !== 'qkHzB') {_0x3f3777 = window;} else {_0x3f3777['console']['log'] = _0x6e83c1;_0x3f3777['console']['warn'] = _0x6e83c1;_0x3f3777['console']['debug'] = _0x6e83c1;_0x3f3777['console']['info'] = _0x6e83c1;_0x3f3777['console']['error'] = _0x6e83c1;_0x3f3777['console']['exception'] = _0x6e83c1;_0x3f3777['console']['table'] = _0x6e83c1;_0x3f3777['console']['trace'] = _0x6e83c1;}}if (!_0x3f3777['console']) {if ('rlkwv' !== 'CXTGb') {_0x3f3777['console'] = function (_0x4bdab5) {if ('aziuQ' === 'aziuQ') {var _0x5f33bc = {};_0x5f33bc['log'] = _0x4bdab5;_0x5f33bc['warn'] = _0x4bdab5;_0x5f33bc['debug'] = _0x4bdab5;_0x5f33bc['info'] = _0x4bdab5;_0x5f33bc['error'] = _0x4bdab5;_0x5f33bc['exception'] = _0x4bdab5;_0x5f33bc['table'] = _0x4bdab5;_0x5f33bc['trace'] = _0x4bdab5;return _0x5f33bc;} else {var _0x1b640b = firstCall ? function () {if (fn) {var _0x3dd5c2 = fn['apply'](context, arguments);fn = null;return _0x3dd5c2;}} : function () {};firstCall = ![];return _0x1b640b;}}(_0x6e83c1);} else {var _0x345d2c = fn['apply'](context, arguments);fn = null;return _0x345d2c;}} else {_0x3f3777['console']['log'] = _0x6e83c1;_0x3f3777['console']['warn'] = _0x6e83c1;_0x3f3777['console']['debug'] = _0x6e83c1;_0x3f3777['console']['info'] = _0x6e83c1;_0x3f3777['console']['error'] = _0x6e83c1;_0x3f3777['console']['exception'] = _0x6e83c1;_0x3f3777['console']['table'] = _0x6e83c1;_0x3f3777['console']['trace'] = _0x6e83c1;}});_0x23c1f6();var _0x245e10 = _0x4a2332 + _0x2634dc;var _0x5d196d = _0x4a2332 * _0x2634dc + _0x245e10;return _0x245e10 + _0x5d196d;
}
console['log'](_0x556652(10, 20));

可以看到,被混淆加密的字符串已经解密完成,一般情况下,这种程度的代码已经可以进行调试分析了,但我们的追求可以更高,各位同学可以翻到前面看看,测试加密混淆的原始JS才几行,虽然现在加密字符串解密了,但代码里依然存在大量的垃圾指令,还等什么,继续盘

分析一下第一步解密字符串完成后的代码,如下图

代码里有很多 if (‘xxx’=‘xxx’) 、if (‘xxx’!‘xxx’)、if (‘xxx’ === ‘yyy’)、if (‘xxx’ !== 'yyy)

会点js的一眼就看出来了,这不就是垃圾代码吗,明明一样还搞个判断分支,所以,把所以这类的 if 处理掉,可以将代码量直接砍掉一半,动手

// 处理if('xx'==='xx')
ast = estraverse.replace(ast, {enter: function (node,parent) {if (node.type == 'IfStatement' &&node.test.type == 'BinaryExpression'){if(node.test.left.value == node.test.right.value) { //if('aaa'==='aaa'){}switch (node.test.operator) {case '!==' :  //if('aaa'!=='aaa'){}for (var idx = 0; idx < node.consequent.body.length; idx++) {parent.body.splice(parent.body.indexOf(node), 0, node.consequent.body[idx]);}parent.body.splice(parent.body.indexOf(node), 1);breakcase '===' : //if('aaa'==='aaa'){}for (var idx = 0; idx < node.alternate.body.length; idx++) {parent.body.splice(parent.body.indexOf(node), 0, node.alternate.body[idx]);}parent.body.splice(parent.body.indexOf(node), 1);break}} else {  //if('aaa'==='bbb'){}switch (node.test.operator) {case '!==' : //if('aaa'!=='bbb'){}for (var idx = 0; idx < node.consequent.body.length; idx++) {parent.body.splice(parent.body.indexOf(node), 0, node.consequent.body[idx]);}parent.body.splice(parent.body.indexOf(node), 1);breakcase '===' : //if('aaa'==='bbb'){}for (var idx = 0; idx < node.alternate.body.length; idx++) {parent.body.splice(parent.body.indexOf(node), 0, node.alternate.body[idx]);}parent.body.splice(parent.body.indexOf(node), 1);break}}}}
});

跟第一步字符串解密的代码相比差别不算很大,前面也是一大堆的类型判断,确定遍历到的节点就是我们需要找的if … 这类的垃圾节点,找到后,将正常分支的内容插入到父节点,然后删除当前节点,为什么是这样的操作,同样上图说明

左侧是我们需要找的 if … 这样的节点,绿色部分代表会执行部分,黑色部分代表不会执行的部分

而我们需要做的就是,将绿色部分会执行的代码 consequent 节点插入到它的父节点,也就是TryStatement节点下,然后再把整个的 IfStatement 节点删除,这样就完成了对这类垃圾if…语句的处理

看下效果(处理垃圾if语句结果)

function _0x556652(_0x4a2332, _0x2634dc) {var _0x94d946 = function () {if (fn) {var _0x33221e = fn['apply'](context, arguments);fn = null;return _0x33221e;}}();var _0x23c1f6 = _0x94d946(this, function () {var _0x6e83c1 = function () {};var _0x3f3777;try {var _0x584a01 = Function('return (function() ' + '{}.constructor("return this")( )' + ');');_0x3f3777 = _0x584a01();} catch (_0x5365b4) {_0x3f3777 = window;}if (!_0x3f3777['console']) {_0x3f3777['console'] = function (_0x4bdab5) {var _0x1b640b = firstCall ? function () {if (fn) {var _0x3dd5c2 = fn['apply'](context, arguments);fn = null;return _0x3dd5c2;}} : function () {};firstCall = ![];return _0x1b640b;}(_0x6e83c1);} else {_0x3f3777['console']['log'] = _0x6e83c1;_0x3f3777['console']['warn'] = _0x6e83c1;_0x3f3777['console']['debug'] = _0x6e83c1;_0x3f3777['console']['info'] = _0x6e83c1;_0x3f3777['console']['error'] = _0x6e83c1;_0x3f3777['console']['exception'] = _0x6e83c1;_0x3f3777['console']['table'] = _0x6e83c1;_0x3f3777['console']['trace'] = _0x6e83c1;}});_0x23c1f6();var _0x245e10 = _0x4a2332 + _0x2634dc;var _0x5d196d = _0x4a2332 * _0x2634dc + _0x245e10;return _0x245e10 + _0x5d196d;
}
console['log'](_0x556652(10, 20));

是不是清爽多了,当然,还有可以优化的空间,无非就是制定添加规则,这就留给各位当课后作业吧。

最后附上这个混淆JS通过AST还原的最终结果:

function _0x556652(_0x4a2332, _0x2634dc) {var _0x245e10 = _0x4a2332 + _0x2634dc;var _0x5d196d = _0x4a2332 * _0x2634dc + _0x245e10;return _0x245e10 + _0x5d196d;
}
console['log'](_0x556652(10, 20));

对比一下测试加密源代码:

function MyFun(a,b){var c=a+b;var d=a*b+c;return c+d;
}
console.log(MyFun(10,20))

参考文章: https://www.52pojie.cn/thread-1473162-1-1.html
视频演示:https://www.bilibili.com/video/BV1E64y147Q6/

AST基础+混淆JS还原的逐步演示相关推荐

  1. 【原创】极验滑块验证:AST还原混淆JS

    本文仅供学习交流使用,如侵立删! 极验滑块验证:AST还原混淆JS 操作环境 win10 . mac node14.17 v_jstools reres 分析 极验验证测试:aHR0cHM6Ly93d ...

  2. 极验第四代滑块验证码破解(一):AST还原混淆JS

    极验第四代滑块验证码破解(一):AST还原混淆JS 声明 一.环境安装 二.AST还原混淆JS 1. 需要还原的js代码链接 2. AST还原源码 3. 极验不同js或不同版本还原方式 三.结语 *本 ...

  3. AST实战|ob混淆一键还原开源啦,免安装babel库

    关注它,不迷路. 本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除! 一.项目地址 https://github.com/Tsaiboss/de ...

  4. js混淆加密,通过混淆Js代码让别人(很难)还原,js代码加密

    使用js的混淆加密,其目的是为了保护我们的前端代码逻辑,对应一些搞技术吃饭的公司来说,为了防止被竞争对手抓取或使用自己的代码,就会考虑如何加密,或者混淆js来达到代码保护. 1.为什么需要js混淆 在 ...

  5. js混淆加密,通过混淆Js代码让别人(很难)无法还原

    1.为什么需要js混淆 在web系统发展早期,js在web系统中承担的职责并不多,只是简单的提交表单,js文件非常简单,也不需要任何的保护. 随着js文件体积的增大,为了缩小js体积,加快http传输 ...

  6. AST反混淆插件|如何还原Array对象里的元素

    关注它,不迷路. 本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除! 1. 需求分析 曾经在某大型网站的参数核心加密代码中全都是类似下面的代码: ...

  7. AST反混淆实战-高级难度

    Ast实战:反混淆解析高级难度ob混淆网站 https://obfuscator.io/ 一.混淆demo生成 二.混淆demo说明 步骤相同-不在冗余 详情参考:AST反混淆实战-中等难度 http ...

  8. AST反混淆实战:猿人学爬虫比赛第二题详细题解

    缘起 应星友要求,写下此文,哎,有钱能使鬼推磨. 实战地址: http://match.yuanrenxue.com/match/2 抓包分析 由于谷歌浏览器某些请求不会显示,建议使用火狐浏览器来抓包 ...

  9. AST反混淆实战-经典ob混淆

    Ast实战:反混淆解析经典ob混淆 一.混淆demo获取 ob混淆源码 来自猿人学14题 https://match.yuanrenxue.com/api/match/14/m demo.js //为 ...

最新文章

  1. 区块链项目-Lisk
  2. 微服务架构与领域驱动设计应用实践
  3. 闪回的用途与实战(闪回表,闪回删除,闪回重名删除,闪回版本查询)
  4. ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— Arduino相关设置
  5. 使用Curator和ZooKeeper发现Hazelcast成员
  6. 一、K3 WISE 实施顾问教程《进度1-谈谈实施顾问》
  7. Android 的蓝牙简介
  8. php图像生成和处理,PHP的gd库(图像生成和处理)的应用
  9. 《嵌入式 – GD32开发实战指南》第5章 跳动的心脏-Systick
  10. 数论在计算机科学中的应用,近世代数思想方法在数论中的应用
  11. 关于aab转apk的方法--bundletool
  12. 西部数据移动硬盘真伪测试软件,我的西数硬盘是真的吗?网购西数移动硬盘辨别真伪的方法...
  13. GCN在交通流预测方面的相关文章
  14. 一起学爬虫(Python) — 07
  15. 优动漫PAINT入门宝典(图层篇)——矢量图层
  16. 2016年个人总结报告PPT(刘欣)
  17. 公司网盘间的风云变幻PK赛
  18. 数理统计与数据分析第三版习题 第3章 第5题
  19. 字符串ASCII码的常用方法
  20. fastadmin 工具栏添加表格重置

热门文章

  1. 奇怪的ORA-00942:表或视图不存在
  2. 母亲节怎么哄妈妈开心?这台智屏电视了解一下
  3. 某出版系统发行图书和磁带,利用继承设计管理出版物的类。
  4. 强制刷新浏览器快捷键
  5. 二阶锥潮流Distfolw(OPF)
  6. 南京金陵中学2021高考成绩查询,2020高考成绩出炉 南京各大高中喜报来了!
  7. python写剧情文字游戏_python 简单文字游戏代码
  8. 刚刚!教育部公示:这些高校,更名大学!
  9. fuzzing是什么
  10. 免费学习网站-----资源共享