AST反混淆实战|某国外混淆框架一小段混淆js还原分析

关注它,不迷路。       

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

1. 需求

我相信做币圈爬虫的兄弟,或多或少的见过类似下面的混淆js:

AST反混淆实战|某国外混淆框架一小段混淆js还原分析_第1张图片

大部分的变量名,主要以 "__p_" 开头,分析下来,其实是ob混淆上面再套了一层壳。

再去掉第一层壳以后,发现还套了一层,它的函数如下:

function __p_1295650017_calc(__p_0552970006, __p_6576181459) {
  switch (__p_3757655876) {
    case 12:
      return !__p_0552970006;
    case 26:
      return -__p_0552970006;
    case -29:
      return __p_0552970006 + __p_6576181459;
    case -6:
      return __p_0552970006 - __p_6576181459;
    case 57:
      return ~__p_0552970006;
    case 35:
      return __p_0552970006 / __p_6576181459;
    case 3:
      return __p_0552970006 * __p_6576181459;
    case 2:
      return typeof __p_0552970006;
    case 6:
      return void __p_0552970006;
  }
}

这个混淆函数很有意思,它有两个形参,函数里面有包含一个全局变量 

__p_3757655876,而这个全局变量,又由下面的函数来控制:

function __p_2245858992(a) {
  a = __p_3757655876 + (__p_3757655876 = a, 0);
  return a;
}

而上面的 混淆函数,它的函数调用有这样的:

__p_1295650017_calc(1940, __p_3757655876 = 26)

函数的结果很明显是 -26;

也有这样的:

__p_1295650017_calc(_0x10e09f(746), "e", __p_2245858992(-29))

函数的结果很明显是 _0x10e09f(746) + "e";

还有一个,就是,对于不同版本的混淆文件,它的这个函数定义是变化着的,如:

function __p_0807818337_calc(__p_9916442306, __p_4970943498) {
  switch (__p_3679524284) {
    case 13:
      return !__p_9916442306;
    case 7:
      return -__p_9916442306;
    case 31:
      return __p_9916442306 + __p_4970943498;
    case 3:
      return __p_9916442306 - __p_4970943498;
    case 21:
      return ~__p_9916442306;
    case 50:
      return __p_9916442306 / __p_4970943498;
    case -47:
      return __p_9916442306 * __p_4970943498;
    case 18:
      return typeof __p_9916442306;
    case 55:
      return void __p_9916442306;
  }
}

2. 共同点

如果想要写出通用的插件,就得找出它的共同点。先来看看有哪些共同点:

  1. 函数名不一样,但都以 _calc 结尾

  2. 函数体结构是一样的,但是 每个case 后面的值不一样。

  3. case体是一样的,都是 return 语句。

很快写出代码:

const getCalcFunction = 
{
  FunctionDeclaration(path)
  {
    let {scope,node} = path;
    
    let {id,body,params} = node;
    
    let name = id.name;
    
    if (!name.endsWith('calc'))
    {
      return;
    }
    
  }
}

因为整个混淆js,只有它是以 "calc" 结尾的,因此,只需简单的判断一下就可以了。

接下来怎么写呢?

根据每个case 里的return语句来写?

3. 隐藏的彩蛋

观察 每个case里的return语句,拿到在线解析网站上观察,只有两种类型:

BinaryExpression  UnaryExpression 类型,而它们有个共同点,都有 operator 这个子节点,即操作符。因此,我们可以使用 一个 Object类型的变量来保存它.即通过每个 case的值 和 operator 来注册一个键值对,保存起来。遍历的时候去取就可以了。

const getCalcFunction = 
{
  FunctionDeclaration(path)
  {
    let {scope,node} = path;
    
    let {id,body,params} = node;
    
    let name = id.name;
    
    if (!name.endsWith('calc'))
    {
      return;
    }
    let newMap = new Object();
    let cases = body.body[0].cases;
    
    for (let eachCase of cases)
    {
      let {test,consequent} = eachCase;
      
      let value = test.value || -test.argument.value;
      
      let operator = consequent[0].argument.operator;
      
      newMap[value] = operator;
    }


    console.log(newMap);
    
  }
}

打印结果:

AST反混淆实战|某国外混淆框架一小段混淆js还原分析_第2张图片

拿到了 case 的值和 它的操作符,再去构造节点就简单了。

这里也许你有个疑问,就是 BinaryExpression  UnaryExpression 它们有个共同的操作符:  "-",如何区分呢?

当然是根据它函数调用的实参个数来区分了。实参是2个的时候,它肯定是UnaryExpression  类型;实参是3个的时候,它肯定是 BinaryExpression 类型。

文件demo在星球里,请星友们完成后续的还原工作:

https://t.zsxq.com/13Sv5Igr1

今天的文章就分享到这里,后续分享更多的技巧,敬请期待。

AST反混淆实战|某国外混淆框架一小段混淆js还原分析_第3张图片

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

你可能感兴趣的:(javascript,前端,java,开发语言,ecmascript)