目标
先将要处理的目标代码放到ast
解析网站中
1.1 找到指定的函数
const fs = require("fs") //文件操作
const {parse} = require("@babel/parser") //将js转成ast格式,{parse}=pase.parse
const traverse = require("@babel/traverse").default //进行遍历节点
const types = require("@babel/types") //用于增加节点
const generator = require("@babel/generator").default // 将ast转成js文件
//0 导入要进行处理的js文件
var originJsCode = fs.readFileSync('001 test.js', {encoding: 'utf-8'})
//0.1 将js转成ast
var originAstCode = parse(originJsCode)
try {
//TODO 1 多级return回调还原,还原之后就要对加密函数进行解密,也就是解密函数的调用
astCode1 = func_return(originAstCode)
console.log("<1> 多级return回调还原已完成")
}catch(error){
console.log(error)
}
/*在上面的是架构,下面的这个函数是具体实现*/
function func_return(astCode){
let visitor = {
//1.对函数定义进行遍历
FunctionDeclaration(path) {
//对函数的两个过滤
//1.1 函数体是块语句
if (types.isBlockStatement(path.node.body)) {
//1.2 函数体内只有一个元素,就是返回语句,只有符合这两个条件才能进行下一步的操作
if (types.isReturnStatement(path.node.body.body[0])) {
}
}
}
}
traverse(astCode,visitor)
fs.writeFileSync("03 operate1.js",generator(astCode).code,{encoding:"utf-8"})
return astCode
}
1.2 找到调用这个函数的地方
1.因为已经定位到了函数①的位置,可以拿到函数①的作用域,所谓作用域就是该函数定义的作用范围,就拿这个函数①来讲:
可以通过path.scope()
来拿到目标函数的作用域
2.拿到作用域可以对该函数名进行绑定,通过绑定可以找到所有引用该函数名的位置,这个位置就是我们想要的调用该函数的位置
那么如何才能拿到真正的调用的js代码,并且验证呢?
实际上上面图片中的paths
拿到的是一个数组,对数组进行遍历(使用map函数),传入的是一个回调函数,第一个参数就是遍历的数组存放的path节点,拿到path节点后再取它的parentPath
才是真正的调用函数代码
实际上拿到的referPath.parentPath
就是ast
中的CallExpression
:
这个函数的调用也有两个特征,referPath.parentPath.type
要为CallExpression
,且referPath.parentPath.callee
要为Identifier
才行
1.3 按照函数返回的规则对调用进行改写
代码如下:
整体来看函数的结构如下:
运行一下看一看提示信息和结果:
来看一看输出的结果:
1.4 循环遍历并确定遍历结束的条件
根据上面的结果存在的问题进行改进,首先来手动确定要遍历的次数,假如要循环遍历3次,试一试:
处理输出结果:
那么如何才能确定循环的次数呢?
经过多次的实验,并没有办法能够确定准确的次数,因为结果一次函数的处理得到的结果很可能还要经过另一个函数进行处理。。。
但可以确定的是函数的名称个数,正如上面输出的结果来看,当输出函数名被引用次数为xxx
时就将定义的函数名称
放入到一个集合当中,如果输出函数名被引用次数为0,进行删除...
时就将这个函数名
从这个集合中删除,只有当这个集合个数为空(也就是0)时,就可以停止循环遍历。
处理如下:
处理结果:
1.5 验证
参考文章: https://jia666666.blog.csdn.net/article/details/120267409
根据文章中的例子来验证:
得到的结果: