Ast入门使用
本帖最后由 QingYi. 于 2022-5-20 11:42 编辑前置条件:
1.一台可以用的电脑
2.双手和大脑
3.JavaScript语法基础
环境:
1.node
2.Pycharm编译器
正文:
1.改变对象属性的访问方式
编写一个简单的加法
方法一:
方法二:
可以看到两种方法都是可以的.
```
function add(a,b){
let res = a + b;
return res;
}
console['log'](add(5,6));
```
来到这个网址
我们可以看到点出来的log的两个属性分别是Identifier 和 false
再来看[]形式的,注意看黄色部分
来编写ast转换代码,以获取当前时间为例
```
Date.prototype.format = function (formatStr) {
var str = formatStr;
var Week = ['日', '一', '二', '三', '四', '五', '六'];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(/MM/, (this.getMonth() + 1) > 9 ? (this.getMonth() + 1).toString() : '0' + (this.getMonth() + 1));
str = str.replace(/dd|DD/, this.getDate() > 9 ? this.getDate().toString() : '0' + this.getDate());
return str;
}
console.log(new Date().format('yyyy-MM-dd'));
```
astcode
执行之后
正常运行
2.JS内置对象的处理
例如这个window可以处理一下
astCode and result
3.常量与标识符的混淆
一个简单的位运算,不懂的看console
result
进行base64转换
也可以把这些常量放到一个数组里面提取出来
对其进行十六进制的混淆
标识符的混淆,也就是作用域有引用的,第五行就没有引用到
我们要找到代码的作用域
生成随机o0O代码
```
function generatorIdentifier(decNum){
let flag = ['O', 'o', '0'];
let retval = [];
while(decNum > 0){
retval.push(decNum % 3);
decNum = parseInt(decNum / 3);
}
let Identifier = retval.reverse().map(function(v){
return flag
}).join('');
Identifier.length < 6 ? (Identifier = ('OOOOOO' + Identifier).substr(-6)):
Identifier == '0' && (Identifier = 'O' + Identifier);
return Identifier;
}
```
全部代码
```
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator").default;
const fs = require('fs');
function base64Encode(e) {
var r, a, c, h, o, types, base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
for (c = e.length, a = 0, r = ''; a < c;) {
if (h = 255 & e.charCodeAt(a++), a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4),
r += '==';
break
}
if (o = e.charCodeAt(a++), a == c) {
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2),
r += '=';
break
}
types = e.charCodeAt(a++),
r += base64EncodeChars.charAt(h >> 2),
r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
r += base64EncodeChars.charAt((15 & o) << 2 | (192 & types) >> 6),
r += base64EncodeChars.charAt(63 & types)
}
return r
}
function hexEnc(code) {
for (var hexStr = [], i = 0, s; i < code.length; i++) {
s = code.charCodeAt(i).toString(16);
hexStr += "\\x" + s;
hexStr +=s;
}
return hexStr
}
const jscode = fs.readFileSync("./demo.js", {
encoding: "utf-8"
});
// 解析为 babel
let ast = parser.parse(jscode);
let bigArr = [];
console.log("start...");
// traverse(ast,{
// MemberExpression(path){
// if(types.isIdentifier(path.node.property)){
// let name = path.node.property.name;
// path.node.property = types.stringLiteral(hexEnc(name));
// }
// path.node.computed = true;
// }
// });
function generatorIdentifier(decNum){
let flag = ['O', 'o', '0'];
let retval = [];
while(decNum > 0){
retval.push(decNum % 3);
decNum = parseInt(decNum / 3);
}
let Identifier = retval.reverse().map(function(v){
return flag
}).join('');
Identifier.length < 6 ? (Identifier = ('OOOOOO' + Identifier).substr(-6)):
Identifier == '0' && (Identifier = 'O' + Identifier);
return Identifier;
}
function renameOwnBinding(path) {
// 自己的对象和全局的对象
let OwnBindingObj = {}, globalBindingObj = {}, i = 0;
path.traverse({
Identifier(p){
let name = p.node.name;
//作用域
let binding = p.scope.getOwnBinding(name);
//三元表达式
binding && generator(binding.scope.block).code == path + '' ?
(OwnBindingObj = binding) : (globalBindingObj = 1);
}
});
//替换
for(let oldName in OwnBindingObj) {
do {
var newName = generatorIdentifier(i++);
} while(globalBindingObj);
OwnBindingObj.scope.rename(oldName, newName);
}
}
// 遍历
traverse(ast, {
MemberExpression(path) {
let name = path.node.property.name;
if (types.isIdentifier(path.node.property)) {
// . to []
path.node.property = types.stringLiteral(name);
}
path.node.computed = true;
},
Identifier(path){
let name = path.node.name;
// 内置
if('eval|parseInt|encodeURIComponent|Object|Function|Boolean|Number|Math|Date|String|RegExp|Array'.indexOf(name) != -1){
path.replaceWith(types.memberExpression(types.identifier('window'), types.stringLiteral(name), true));
}
},
NumericLiteral(path){
// value=>1000 key==>666 cipherNum===>370
let value = path.node.value;
let key = parseInt(Math.random() * (999999 - 100000) + 100000, 10);
let cipherNum = value ^ key;
path.replaceWith(types.binaryExpression('^', types.numericLiteral(cipherNum), types.numericLiteral(key)));
//替换后的节点里也有numericLiteral节点,会造成死循环,因此需要加入path.skip()
path.skip();
},
StringLiteral(path){
let encStr = types.callExpression(
types.identifier('atob'), );
path.replaceWith(encStr);
path.skip();
},
StringLiteral(path){
let cipherText = base64Encode(path.node.value);
// 检查是否数组里面已经存在
let bigArrIndex = bigArr.indexOf(cipherText);
let index = bigArrIndex;
if(bigArrIndex == -1){
let length = bigArr.push(cipherText);
index = length -1;
}
let encStr = types.callExpression(
types.identifier('atob'),
[types.memberExpression(types.identifier('arr'),
types.numericLiteral(index), true)]);
path.replaceWith(encStr);
},
'Program|FunctionExpression|FunctionDeclaration'(path) {
renameOwnBinding(path);
},
});
bigArr = bigArr.map(function(v){
return types.stringLiteral(v);
});
//声明arr 并且加入到最前面
bigArr = types.variableDeclarator(types.identifier('arr'), types.arrayExpression(bigArr));
bigArr = types.variableDeclaration('var', );
ast.program.body.unshift(bigArr);
let oneCode = generator(ast).code;
fs.writeFile('./AfterAstCode.js', oneCode, (err)=>{});
console.log("end...");
``` AST不是需要解析Token,再调用Parser么,略读了下,没有涉及到呢,这个跟我了解的AST不是同一个东西么 学习到了,感谢感谢 这不是小肩膀的教程吗?
{:301_1008:} 谢谢楼主分享 小葵花课堂开课了,感谢up主分享好的教程 谢谢楼主分享 谢谢分享 感谢大佬分享教程 感谢教程, 对js 反编译很有用
页:
[1]
2