回答

收藏

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型

信息分享 信息分享 1377 人阅读 | 0 人回复 | 2021-01-17





我们今天来看看,如何将一个 BinaryExpression 类型的节点转换成 CallExpression 类型 的节点。即将代码:
var a = 123 | 456;



转换为:



var a = function (s, h) {  return s | h;}(123, 456);






为什么要这么做,因为一个BinaryExpression 类型的节点(操作符两边都是 Literal 类型的节点)很容易就给还原了,如果将其转变成一个CallExpression 类型 的节点的话,似乎还原要困难点。






01








节点比对












这是一个技巧,就是将一个节点转变成另外一个节点的时候,可以分别将其解析,看看节点之间有什么变化,然后再缺啥补啥即可。




首先来看



var a = 123 | 456;



这段代码解析成AST是怎样的结构:











就是一个简单的变量定义,只不过其 init 是一个 BinaryExpression 类型的节点,再看



var a = function (s, h) {
  return s | h;
}(123, 456);



这段代码解析成AST是怎样的:










除了 init,其他的没什么变化,因此只需要对 init 进行操作即可。








02




一步一步,缺啥补啥




从上面的截图可以看到,init 下面的节点变化了,因此我们需要构造这些节点再进行替换。





在这里遍历 VariableDeclarator,写出如下的插件:



const visitor = {
  "VariableDeclarator"(path)
  {
    binary2func(path)
  },
}



再完成这个  binary2func 函数,当然是先判断类型了,如下:
function binary2func(path)
{
  const init_path = path.get('init');
  if (!init_path.isBinaryExpression()) return;
 }



init 的类型变了,所以将 type直接修改下:



init_path.node.type = "CallExpression";



还需要构造两个节点,CallExpressionarguments




先看 arguments 节点,有两个 elements 这 两个 节点就是 BinaryExpression 节点的 leftright 子节点,如下图。

left right :









arguments







因此,可以写出如下的代码:



let {operator,left,right} = init_path.node;
init_path.node.arguments = [left,right];





再来看 CallExpression  节点:










如上图,这个节点略微复杂了点,其实也不难,构造就完事了。





1.id 的值是 null,一样的生成一个 null节点:



let id = null;





2.params 包含两个 元素,也简单:










let frist_arg  = t.Identifier('s');
  let second_arg = t.Identifier('h');
  let params = [frist_arg,second_arg];








3.再看 body下面的body节点,可以看到,它只包含了一个元素:











一个 ReturnStatement 类型的节点,因此先构造这个,但是里面还有一个 节点,所以由小及大,优先创建 BinaryExpression 节点:











BinaryExpression 节点 已经在之前的文章中多次创建了,只需要关注 operator 、left和right。

这在上面都已经给出了:



let args = t.BinaryExpression(operator,frist_arg,second_arg);





再通过构造好的 节点,创建一个 ReturnStatement 节点:



let return_state = t.ReturnStatement(args);





再往上看 body,是一个BlockStatement 的节点:











构造起来也很容易:



let body = t.BlockStatement([return_state]);





继续往上走,是一个 FunctionExpression 类型 的节点,构造也容易:











在这里是 init_path.node.callee,因此直接赋值:



init_path.node.callee = t.FunctionExpression(id ,params,body);





4.所有的工作都已完成,下面是完整的代码:











结果如下:














希望文章对大家有帮助,谢谢阅读。




欢迎关注本人公众号交流学习更多AST相关的知识。









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
分享到:
回复

使用道具 举报