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

“继续学习操作AST

我们今天来看看,如何将一个 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是怎样的结构:

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第1张图片

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

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

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第2张图片

除了 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";

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

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

left 与 right :

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第3张图片

arguments :

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第4张图片

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

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

再来看 CallExpression  节点:

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第5张图片

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

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

let id = null;

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第6张图片

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

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第7张图片

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第8张图片

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

这在上面都已经给出了:

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

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

let return_state = t.ReturnStatement(args);

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第9张图片

构造起来也很容易:

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

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第10张图片

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

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

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

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第11张图片

结果如下:

JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型_第12张图片

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

你可能感兴趣的:(css,javascript,js,python,java)