JavaScript 代码混淆实战(二):将 BinaryExpression 类型转换为 CallExpression 类型 Web逆向 原创



我们今天来看看,如何将一个 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";


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


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

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相关的知识。




最新回复 (0)
返回