# AST 思考

本篇重在介绍关于 ast 还原代码的一些逻辑。

首先,我们知道,以目前常见的混淆来讲,大部分其实是通过不断地调用一个类似生成函数的地方,来调用。

我们在分析时,往往想看到其直接调用的位置,那么,我们就需要将所谓的生成入口写入内存中,然后直接调用进而达到直接将调用的结果展示出来。

通常,普通的混淆生成入口有如下四种:

  • 数组型
  • 函数形
  • 对象型
  • 组合型
# 数组型

所谓数组型,就是类似以数组的形式,通过数组下标访问。例如:

var arrlist = [1,2,3];
var b = arrlist[1];

对于该种情况,我们抽象为如下四步:

  • 确定要遍历的对象,这里是 VariableDeclarator。
  • 接下来是判断 init 部分的类型,因为可能还有许多声明其他变量的部分,我们希望的是只遍历右边是数组类型的。
  • 然后是将其写入内存,直接采用 eval 的方式 (最好将其写入全局变量中)。
  • 最后是访问数组的作用域,找到其引用,然后直接替换节点即可。
# 函数形

所谓函数形,即声明的类型是函数,通过函数访问。

// 类型 1
var add1 = function(a,b){
	return a + b;
};
// 类型 2
function add2(a,b){
    return a + b;
};
var c = add1(1,2) + add2(3,4);

该类情况与数组大致相同,只不过在遍历时候还需考虑第二种情况:

  • 确定要遍历的对象,这里是 FunctionExpression,针对情况 1,可以直接通过 path.parent.id.name 获取函数昵称,针对情况 2,可以通过 path.node.id.name 获取函数昵称。
  • 接下来是对函数体结构的判断,必须要有该函数的引用,其次,该函数可以通过传入的值,直接计算出结果。
  • 然后是将其写入内存,直接采用 eval 的方式 (最好将其写入全局变量中)。
  • 最后是访问函数的作用域,找到其引用,然后直接替换节点即可。
# 对象型

所谓对象型,就是声明了一个大对象,通过调用的方式访问。

var obj = {
	'a':123,
	'b':456,
};
var c = obj['a'];

这个的本质与数组可以说是一模一样,在此不做赘述。

# 组合型

所谓的组合型,就是通过对象、数组、函数的组合方式,使得其变得复杂化,进而加大一键脱混淆的难度,进而使得我们需要针对型的修改插件,才能使得混淆得以脱掉。

补充:其实上述也可以通过访问调用然后遍历其作用域,进而找到其声明部分替换,但是假如引用作用域中包含了对原对象的修改,那么在遍历时,可能会造成一定的难度。

目前来讲个人的解混淆思路倾向于写入式:

针对不同的情况,若是一个对象、函数、变量,经常被调用,那么我会将其写入内存,然后直接通过访问内存中的属性来修改节点。

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

Mr2 WeChat Pay

WeChat Pay