上一篇 腾讯滑块VMP反编译(一) 中已经把最原始的流程部分反编译,但反编译后的函数还是原来58分支的绑定函数。

函数节点反编译

分析原理

到这里仔细分析一下腾讯VMP构造函数的原理:

可以看到,调用新函数实质上就是修改传入vm的各个参数,然后调用vm,由于chaos_vm被传入了不同的参数,就会跳入不同的指令中操作。

在这里可以思考一下,既然调用了__TENCENT_CHAOS_VM,那我们先构造一个空函数function xx(){},然后调用__TENCENT_CHAOS_VM,向函数的block块里面丢函数的代码句不就可以了吗?

修改VM函数,适配不同的代码块

先给vm函数增加一个传入值funcBody,这里的funcBody用于存放VM函数内部指令生成的最终代码的ast节点(即主程序的astBody,以及函数语句的block部分等),同时把第一篇中指令内部的各种astBody.push改为funcBody.push

同样的,在初始化调用那一部分也要传入整个主程序的body(即astBody)

这时候,只要执行vm时候,传入在funcBody传入空数组[],在vm函数执行完毕后,这个空数组就会被填充相应的ast节点。

储存函数常量

上面说到,不同的函数,K m U A E F Y c都是不同的,因此我们要在函数定义时候把它储存起来,等到相应函数调用时候再取出它们。这里我使用map的方法储存。

文件头部初始化map:

给函数增加一个变量名:

根据上面的分析去构造A, 同时初始化一个空数组func_bbbbody,用来存放这个函数的函数体,并把arguments传递给变量,防止多个函数引用同一个函数的arguments引起混乱:

以刚初始化的func_bbbbody为block构造函数,同时以函数名为key,将func_bbbbody和该函数相应的参数储存并映射:

执行vm,填充func_bbbbody

根据函数名取出上面map中的func_bbbbody和args,并传入vmp中执行:

至此,函数节点还原的流程基本完毕。

运行ast后继续补缺失的case。

47、36、59、0、8、64、54

if else节点反编译

分析原理

根据判断条件的true或false改变g以修改后续指令执行位置,从而实现if else的效果(这里先不考虑for)。

想要还原,就可以参考函数的还原。先初始化两个数组分别代表true、false分支的block,然后调用chaos_vm,分别传入true时g的变化值,false时g的变化值,填充两个block进行还原。

实现


注意事项

从上图可以看出,这里直接把if else两个分支暴力执行了一遍,如果遇到以下代码:

if(a>b){
	c;
} else {
	d;
};
e;
f;

则会变成:

if(a>b){
	c;
	e;
	f;
} else {
	d;
	e;
	f;
}

因此,上图只是一个粗略实现,实际实现要用数组去储存if/else其中一个分支所经过的所有g,然后在另一个分支执行时去判断g是否存在在数组中,如果存在且不为判断条件,则这个g为if else的交汇点,如果存在且为判断条件,则为for循环的判断语句,和控制流非常相似。代码实现会在for循环部分一起说。


if else写完后再补几个case。

41

2(重要)


到这里case暂时补完了,反编译后效果如下:

可以看到,函数以及函数内部的函数都已经被还原,if else也已经被还原。下面那些还未被还原的函数是因为myArg_1[myArg_65]["call"].apply还未处理,无法跳入其它函数。

待续。


后面学习了遗心大佬的思路,给vm添加上起始和终止ip及判断条件,发现第三篇没什么写的了,具体怎么搞在上一篇的付费课里有,摆烂= =