AST反混淆实战|datadome混淆代码控制流还原分析

关注它,不迷路。       

本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!

1.控制流代码

在还原datadome的字符串以后,发现了一下控制流,大部分类似如下:

ca49c5f931bee8f68369218e0c26d0b3.png

所有需要处理的循环都是这种结构,即循环体只有两个节点,一个是switch节点,另外一个是break节点,和ob混淆的控制流有点类似。

2.预处理

想要还原控制流,需要获取每次switch语句discriminant的值,而它的初始值是首先需要获取的,因此这里最好把for循环的init给提取到前面来,方便我们获取初始值。插件代码如下:

const aheadInitNodeOfFor = {
  /**  @param  {NodePath} path */  //每个插件前都要加哈。
  ForStatement(path) {


    let { init, test, update, body } = path.node;


    if (!types.isBooleanLiteral(test, { "value": true }) ||
      update != null || body.body.length != 2) {
      return;
    }


    let firstNode = body.body[0];
    let secondNode = body.body[1];


    if (!types.isSwitchStatement(firstNode) || !types.isBreakStatement(secondNode)) {
      return;
    }


    if (types.isAssignmentExpression(init)) {
      path.insertBefore(types.ExpressionStatement(init));
    }
    else if (types.isVariableDeclaration(init)) {
      path.insertBefore(init);
    }
    else {
      return;
    }
    path.node.init = null;


  },
}




traverse(ast, aheadInitNodeOfFor);

处理后,还需要将变量定义给分离出来,方便后面的查找:

const DeclaratorToDeclaration =
{
  VariableDeclaration(path) {
    let { parentPath, node } = path;
    if (!parentPath.isBlock()) {
      return;
    }
    let { declarations, kind } = node;


    if (declarations.length == 1) {
      return;
    }


    let newNodes = [];


    for (const varNode of declarations) {
      let newDeclartionNode = types.VariableDeclaration(kind, [varNode]);
      newNodes.push(newDeclartionNode);
    }


    path.replaceWithMultiple(newNodes);


  },
}


traverse(ast, DeclaratorToDeclaration);

继续分析,看到这样的代码:

74167970a00a735b6db21b6d8aaa4a2e.png

case语句里面是空的,因为它在最后一个case,其实就是退出了switch语句,因此加个break节点就好:

const addBreakNodeOfemptyCase =
{
  SwitchCase(path) {
    let { consequent } = path.node;


    if (consequent.length == 0) {
      console.log(path.toString());
      path.node.consequent.push(types.BreakStatement());
    }


  },
}


traverse(ast, addBreakNodeOfemptyCase);

处理后的样子:

06123f31d0ba0fb9bcefb9920d2aaa1e.png

接下来,就可以愉快的去控制流了。

3.去控制流思路

  1. 获取到初始值,可以写个方法与插件分离出来

  2. 匹配这个初始值,获取该 SwitchCase 的 consequent子节点的内容,并根据最后一个节点类型 更新 初始值;

  3. 如果最后一个节点类型是break语句或者return语句,则直接退出循环;

4.反混淆源码

本文的demo及代码,放在星球里,需要的请自取。

https://t.zsxq.com/e23xV

今天的分享就到这里,感谢阅读。

欢迎加入知识星球,学习更多AST和爬虫技巧。

20ae2b0f4e5cab608b7a3b8c4d0479c6.jpeg

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值