下面是某滑块的关键加密代码的部分截图:
放眼望去,尽是两个正(负)数的异或,明明可以直接得出结果,为啥要弄成这种形式呢?这么做也许是为了吓退新手们吧。如果你会操作AST,这种代码分分钟就给还原了。所谓知己知彼,百战不殆,我们下面来简单看看这样的代码是怎么生成的。
随便取两个数(符号相同)进行异或,例如:
-16892453 ^ -16892095 = ?
得到结果:
-16892453 ^ -16892095 = 666
所以,上面截图里的代码不过是将一个 NumericLiteral 类型的节点(666) 拆分为 一个 BinaryExpression 类型的节点(-16892453 ^ -16892095)。
目的
处理前节点:
var a = 666,b = 777;
处理后节点:
var a = -12270231 ^ -12269581,b = -14267009 ^ -14266762;
思路
首先要明确的是遍历 NumericLiteral 这样的节点
const NumericToBinary = { NumericLiteral(path) { // somecodes }, }
需要生成一个BinaryExpression节点:
let node = types.BinaryExpression("^",left,right);
节点替换:
path.replaceWith(node);
基本上都是这套路,很快,就写出了代码:
const NumericToBinary = {
NumericLiteral(path)
{
let value = path.node.value;
let left = 0-Math.floor(Math.random() * 10000000 + 10000000);
let right = value ^ left;
let node = types.BinaryExpression("^",types.valueToNode(left),types.valueToNode(right));
path.replaceWith(node);
},
}
运行后发现内存溢出了:
一般出现这样的情况是因为替换的节点(node)包含了正在遍历的类型(例如NumericLiteral),因此程序会一直递归遍历下去,直到内存溢出。
想要解决这样的问题也很简单,加一行代码即可:
path.stop()
插件源代码
所以再次修改后的代码:
const types = require("@babel/types");
const NumericToBinary = {
NumericLiteral(path)
{
let value = path.node.value;
let left = 0-Math.floor(Math.random() * 10000000 + 10000000);
let right = value ^ left;
let node = types.BinaryExpression("^",types.valueToNode(left),types.valueToNode(right));
path.replaceWith(node);
path.stop();
},
}
运行后不再报内存溢出的错误,结果也正常了。
思考:如果不用 path.stop(),还有其他的方法没?
有点,我们给出一个退出条件即可。例如,遍历前的节点NumericLiteral,就是一个NumericLiteral类型的节点,没有前缀 "-",而替换后的NumericLiteral 节点,有了前缀"-"之后变成了 UnaryExpression 节点。因此,根据这个特征,可以写出退出代码:
const NumericToBinary = {
NumericLiteral(path)
{//添加退出条件
if (path.parentPath.isUnaryExpression({operator:"-"})) return;
let value = path.node.value;
let left = 0-Math.floor(Math.random() * 10000000 + 10000000);
let right = value ^ left;
let node = types.BinaryExpression("^",types.valueToNode(left),types.valueToNode(right));
path.replaceWith(node);
},
}
本文完。
欢迎关注本人微信公众号,学习交流更多AST相关的知识。