关注它,不迷路。

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

一.实战地址

https://match.yuanrenxue.com/match/2

二.混淆js分析与还原

上面的网站打开后,按下F12,停留在debugger位置,往上一层堆栈,可以看到混淆的js,格式化,然后复制下来,从头看到尾,大致的阅读下js,寻找可还原的点,发现了这里:

ios代码混淆 过审 混淆代码还原_ios代码混淆 过审

这是一大片的十六进制字符串,可以先将其还原:

const simplifyLiteral = {
  NumericLiteral({node}) {
    if (node.extra && /^0[obx]/i.test(node.extra.raw)) {
      node.extra = undefined;
    }
  },
  StringLiteral({node}) 
  {
    if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
      node.extra = undefined;
    }
  },
}

还原后的效果如下:

ios代码混淆 过审 混淆代码还原_css_02

还原后,就是想办法把这些字符串进行回填,举个简单的例子:

OooIi1 = "0x1fd"
......
$dbsm_0x42c3(OooIi1, QQQO0q)

===>

$dbsm_0x42c3("0x1fd", QQQO0q)

这样就能删除一大堆代码,在这里,可以利用scope进行替换:

const replaceLiteral = {
  AssignmentExpression(path)
  {
    let {scope,parentPath,node} = path;
    let {left,right,operator} = node;
    if (!types.isIdentifier(left) || operator != "=" ||
        !types.isLiteral(right))
    {
      return;
    }
    
    let binding = scope.getBinding(left.name);
    if (!binding) return;   //SCOPE可能为空,目前看的话,只有函数定义的形参和变量定义才有scope
    
    let {constantViolations,referencePaths} = binding;
    
    if (binding.constantViolations.length == 1 &&
       path == binding.constantViolations[0])
    {
      for (let referPath of referencePaths)
      {
        referPath.replaceWith(right);
      }
      path.remove();
      binding.path.remove();  //binding.path  都是形参,所以可以直接删除。
    }
   },
    "BinaryExpression"(path)
    {
      if(path.isUnaryExpression({operator:"-"}) || 
         path.isUnaryExpression({operator:"void"}))
      {
        return;
      }
      const {confident,value} = path.evaluate();
      if (!confident || value == "Infinity") return;
      path.replaceWith(types.valueToNode(value));
    },
}

还原后,效果如下:

ios代码混淆 过审 混淆代码还原_css_03

这是一系列的逗号表达式,再将其还原:

const resolveSequence = 
{
  SequenceExpression(path)
  {
    let {scope,parentPath,node} = path;
    let expressions = node.expressions;
    if (parentPath.isReturnStatement({"argument":node}))
    {
      let lastExpression = expressions.pop();
      for (let expression of expressions)
      {
        parentPath.insertBefore(types.ExpressionStatement(expression=expression));
      }
      
      path.replaceInline(lastExpression);
    }
    else if (parentPath.isExpressionStatement({"expression":node}))
    {
      let body = [];
      expressions.forEach(express=>{
            body.push(types.ExpressionStatement(express));
        });
      path.replaceInline(body);
    }
    else
    {
      return;
    }
    
    scope.crawl();
  }
}

还原后效果如下:

ios代码混淆 过审 混淆代码还原_ios代码混淆 过审_04

熟悉ob混淆的朋友可以看到,已经非常像了,但是,还不能用我开源的那个一键还原,需要做如下变形:

_0x434ddb = {};

===>

var _0x434ddb = {};

因此,写下插件,进行变形,使之成为ob混淆,便于我们使用一键还原工具,代码如下:

function deleteParams(path,name)
{
  let funcPath = path.getFunctionParent();
  let params = funcPath.node.params;
  for (let i=0; i<params.length; i++)
  {
    if (params[i].name == name)
    {
      funcPath.node.params.splice(i,1);
      break;
    }
  }  
}




const AssignToVar = 
{//猿人学第二题专用插件,无法通用
  AssignmentExpression(path)
  {
    let {scope,node,parentPath} = path;
    if (!parentPath.isExpressionStatement()) return;
    let {left,operator,right} = node;
    if (!types.isIdentifier(left) || operator != "=" ||
        !types.isObjectExpression(right))
    {
      return;
    }
    
    let {properties} = right;
    if (properties.length != 0) return;
    
    let VarDeclarNode = types.VariableDeclarator(left,right);
    let VarDeclarationNode = types.VariableDeclaration("var",[VarDeclarNode]);
    
    parentPath.replaceWith(VarDeclarationNode);
    
    let name = left.name;
    deleteParams(path,name);
    
    scope.traverse(scope.block,{
      AssignmentExpression(_path)
      {
        let {node,parentPath} = _path;
        if (!parentPath.isExpressionStatement()) return;
        let {left,operator,right} = node;
        if (!types.isIdentifier(left) || operator != "=" ||
        !types.isIdentifier(right,{name:name}))
        {
          return;
        }
        deleteParams(_path,left.name);
        scope.rename(left.name,name);
        parentPath.remove();
      }
    })
  }
      
}

上面的代码处理后,变成了这样:

ios代码混淆 过审 混淆代码还原_javascript_05

这样,就给变量加上了var,接下来,就可以使用ob混淆一键还原工具了,

https://github.com/Tsaiboss/decodeObfuscator

运行后结果变这样:

setInterval(function () {
  $dbsm_0x1a0b2e();
}, 4000);

傻眼了,啥也没了,还怎么分析?这个时候,打开一键还原项目里的main.js,看到这里:

ios代码混淆 过审 混淆代码还原_ios代码混淆 过审_06

它说可能会误删代码,因此,我们将这行代码屏蔽试试:

ios代码混淆 过审 混淆代码还原_javascript_07

代码还剩343行,具体的cookie赋值位置也可以看到:

ios代码混淆 过审 混淆代码还原_编程语言_08

本文完整分析就到这里,AST还原其实很简单,无非就是分析前后节点的变化。完整AST代码及数据请求代码我放在星球里了,需要的请自取:

https://t.zsxq.com/jAIIYNF

今天的内容就介绍到这里,如果你有更好玩的工具,欢迎分享给我们,感谢。