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

“继续学习操作AST

我们今天来看看,如何将一个 BinaryExpression 类型的节点转换成

CallExpression 类型 的节点。即将代码:

var a = 123 | 456;

  

转换为:


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

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

01

节点比对

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

首先来看 var a = 123 | 456; 这段代码解析成AST是怎样的结构:

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


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

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

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

02


一步一步,缺啥补啥

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

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


   
  1. const visitor = {
  2. "VariableDeclarator"(path)
  3. {
  4. binary2func(path)
  5. },
  6. }

再完成这个  binary2func 函数,当然是先判断类型了,如下:


   
  1. function binary2func(path)
  2. {
  3. const init_path = path.get('init');
  4. if (!init_path.isBinaryExpression()) return;
  5.  }

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

init_path.node.type = "CallExpression";

  

还需要构造两个节点,CallExpression 和  arguments

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

left 与 right :

arguments :

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


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

再来看 CallExpression  节点:

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

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

let id = null;

  

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


   
  1. let frist_arg = t.Identifier('s');
  2. let second_arg = t.Identifier('h');
  3. 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.所有的工作都已完成,下面是完整的代码:

结果如下:

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

文章来源: blog.csdn.net,作者:悦来客栈的老板,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq523176585/article/details/111714299

(完)