目标
http://sgnec.sgcc.com.cn/
无法打开F12,经查找知道是使用了https://github.com/theajack/disable-devtool
本来是想用hook的方式绕过,结果发现js被混淆了,大致如下,


所以需要临时来学习一下AST解混淆
基础语法
解混淆方式
字符串还原
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const types = require("@babel/types")
let code = "console['\u006c\u006f\u0067']('\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u006f\u0072\u006c\u0064\u0021')" const ast = parser.parse(code)
const visitor = { StringLiteral(path) { delete path.node.extra.raw } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
表达式还原
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const types = require("@babel/types")
const code = ` const a = !![]+!![]+!![]; const b = Math.floor(12.34 * 2.12) const c = 10 >> 3 << 1 const d = String(21.3 + 14 * 1.32) const e = parseInt("1.893" + "45.9088") const f = parseFloat("23.2334" + "21.89112") const g = 20 < 18 ? '未成年' : '成年' ` const ast = parser.parse(code)
const visitor = { "BinaryExpression|CallExpression|ConditionalExpression"(path) { const {confident, value} = path.evaluate() if (confident){ path.replaceInline(types.valueToNode(value)) } } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
删除未使用变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default
const code = ` const a = 1; const b = a * 2; const c = 2; const d = b + 1; const e = 3; console.log(d) ` const ast = parser.parse(code)
const visitor = { VariableDeclarator(path){ const binding = path.scope.getBinding(path.node.id.name);
if (!binding || binding.constantViolations.length > 0) { return; }
if (!binding.referenced) { path.remove(); }
} }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
删除冗余逻辑代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const types = require('@babel/types');
const code = ` const example = function () { let a; if (false) { a = 1; } else { if (1) { a = 2; } else { a = 3; } } return a; }; ` const ast = parser.parse(code)
const visitor = { enter(path) { if (types.isBooleanLiteral(path.node.test) || types.isNumericLiteral(path.node.test)) { if (path.node.test.value) { path.replaceInline(path.node.consequent.body); } else { if (path.node.alternate) { path.replaceInline(path.node.alternate.body); } else { path.remove() } } } } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
switch-case 反控制流平坦化
1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const types = require("@babel/types") const fs = require("fs");
const code = `const _0x34e16a = '3,4,0,5,1,2'['split'](','); let _0x2eff02 = 0x0; while (!![]) { switch (_0x34e16a[_0x2eff02++]) { case'0': let _0x38cb15 = _0x4588f1 + _0x470e97; continue; case'1': let _0x1e0e5e = _0x37b9f3[_0x50cee0(0x2e0, 0x2e8, 0x2e1, 0x2e4)]; continue; case'2': let _0x35d732 = [_0x388d4b(-0x134, -0x134, -0x139, -0x138)](_0x38cb15 >> _0x4588f1); continue; case'3': let _0x4588f1 = 0x1; continue; case'4': let _0x470e97 = 0x2; continue; case'5': let _0x37b9f3 = 0x5 || _0x38cb15; continue; } break; } ` const ast = parser.parse(code)
const visitor = { WhileStatement(path) { let switchNode = path.node.body.body[0]; let arrayName = switchNode.discriminant.object.name; let prevSiblings = path.getAllPrevSiblings(); let array = [] prevSiblings.forEach(pervNode => { let {id, init} = pervNode.node.declarations[0]; if (arrayName === id.name) { let object = init.callee.object.value; let property = init.callee.property.value; let argument = init.arguments[0].value; array = object[property](argument) } pervNode.remove(); });
let replace = []; array.forEach(index => { let consequent = switchNode.cases[index].consequent; if (types.isContinueStatement(consequent[consequent.length - 1])) { consequent.pop(); } replace = replace.concat(consequent); } ); path.replaceWithMultiple(replace); } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const types = require("@babel/types") const fs = require("fs");
const code = `const _0x34e16a = '3,4,0,5,1,2'['split'](','); let _0x2eff02 = 0x0; while (!![]) { switch (_0x34e16a[_0x2eff02++]) { case'0': let _0x38cb15 = _0x4588f1 + _0x470e97; continue; case'1': let _0x1e0e5e = _0x37b9f3[_0x50cee0(0x2e0, 0x2e8, 0x2e1, 0x2e4)]; continue; case'2': let _0x35d732 = [_0x388d4b(-0x134, -0x134, -0x139, -0x138)](_0x38cb15 >> _0x4588f1); continue; case'3': let _0x4588f1 = 0x1; continue; case'4': let _0x470e97 = 0x2; continue; case'5': let _0x37b9f3 = 0x5 || _0x38cb15; continue; } break; } ` const ast = parser.parse(code)
const visitor = { WhileStatement(path) { let switchNode = path.node.body.body[0]; let arrayName = switchNode.discriminant.object.name; let bindingArray = path.scope.getBinding(arrayName); let init = bindingArray.path.node.init; let object = init.callee.object.value; let property = init.callee.property.value; let argument = init.arguments[0].value; let array = object[property](argument)
let autoIncrementName = switchNode.discriminant.property.argument.name; let bindingAutoIncrement = path.scope.getBinding(autoIncrementName); bindingArray.path.remove(); bindingAutoIncrement.path.remove();
let replace = []; array.forEach(index => { let consequent = switchNode.cases[index].consequent; if (types.isContinueStatement(consequent[consequent.length - 1])) { consequent.pop(); } replace = replace.concat(consequent); } ); path.replaceWithMultiple(replace); } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
|
参考
Ast - 反混淆(基础篇)
逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
ast解析器
非官方文档
disable-devtool