jsvmp

混淆的另一种常见毒瘤:jsvmp。jsvmp:全称 Virtual Machine based code Protection for JavaScript,即 JS 代码虚拟化保护方案。

八股文很多,但是了解很必要! 如果要是不理解一些运行基础的话,它理解起来会比较吃力。下面,我们浅浅的给大家介绍一下,目的就是让大家心里先有个数,jsvmp它到底在干些什么。以后我们再说处理它的方法。

首先,常见的jsvmp的实现方法,你可以理解为:就是自己写了一段代码解释器,用来解释自己的代码
而这个自己的代码:可以是密文,也可以是所谓的明文。

我们先不用管所谓的 寄存器 指针 指令 是什么东西。先给大家分析一段简单的代码。

var b = 1000;
var c = 1000;
var d = a + b;
var e = d + c;
js

上面这段代码,我们现在去理解它做了什么;
用我们人类的语言对它进行翻译(我们先不考虑变量提升那些乱七八糟的事儿):

  • 声明一个变量 a, 并且给a赋值 1000;
  • 声明一个变量 b, 并且给b赋值 1000;
  • 声明一个变量 c, 并且给c赋值 1000;
  • 声明一个变量 d, 计算 a+b 的值, 将值赋值给 d;
  • 声明一个变量 e, 计算 d+c 的值, 将值赋值给 d;

那么下面,我们在把下面的分段再翻译成代码:

var a;
a = 1000;
var b;
b = 1000;
var c;
c = 1000;
var d;
? = a + b;
d = ?;
var e;
e = d + c;
js

OK, 我们完成了,下面我们开始定义指令
我们假设赋值指令为 66, 加和指令为 88,声明指令为 110(爱取什么名字取什么名字)
如果按照从上到下的顺序,我们就可以将他们的操作变成指令性的[用|分割左侧和右侧]

110 --- var | a
66 --- a |  1000
110 --- var | b
66 --- b |  1000
110 --- var | c
66 --- c |  1000
110 --- var | d
88 --- a | b  -----> ?
66 --- d | ?
110 --- var | e
88 --- d | c   -----> ?
66 --- e | ?(此处的d 与 c的和)
js

下一步,我们将每一步,都压入一个数组中,我们就得到了这样一个数组:

_stack = [
  [110, 'var', 'a'],
  [66, 'a', 1000],
  [110, 'var', 'b'],
  [66, 'b', 1000],
  [110, 'var', 'c'],
  [66, 'c', 1000],
  [110, 'var', 'd'],
  [88, 'a', 'b'],
  [66, 'd', '?'],
  [110, 'var', 'e'],
  [88, 'd', 'c'],
  [66, 'e', '?'],
]
js

然后我们就可以靠接下来这个数组去完成我们刚才的代码了,其实这个代码写起来就相当简单了

var register;    // 这个就当做是问号的存储位置 即寄存器
var variable = {};    // 这个就当做是var变量的存储位置。由于没有其他声明方式的存在,所就不写其他的了
for(let i=0;i<_stack.length;i++){
  instruct = _stack[i][0]   // 我这么写是让大家看得懂,真正的时候,人家才不会那么好心
  left = _stack[i][1]   // 我这么写是让大家看得懂,真正的时候,人家才不会那么好心
  right = _stack[i][2]   // 我这么写是让大家看得懂,真正的时候,人家才不会那么好心

  if(instruct === 110){
      variable[right] = '';
  }
  if(instruct === 66){
      if(right === '?'){
          variable[left] = register;
      }
      else{
          variable[left] = right;
      }
  }
  if(instruct === 88){
      register = variable[left] + variable[right];
  }
}
js

这样代码就写完了,然后我们在简单的做一些加工, 封装到自执行函数里,注释去掉,再压缩一下

!function(_stack){var register;var variable={};for(let i=0;i<_stack.length;i++){instruct=_stack[i][0];left=_stack[i][1];right=_stack[i][2];if(instruct===110){variable[right]=''}if(instruct===66){if(right==='?'){variable[left]=register}else{variable[left]=right}}if(instruct===88){register=variable[left]+variable[right]}};console.log(variable)}([[110,'var','a'],[66,'a',1000],[110,'var','b'],[66,'b',1000],[110,'var','c'],[66,'c',1000],[110,'var','d'],[88,'a','b'],[66,'d','?'],[110,'var','e'],[88,'d','c'],[66,'e','?']])
js

目前我们就用这样的代码实现起初的效果,这样的代码调试起来会非常困难。

还可以思考以下问题:

这还是基于我只写了三个操作
那其他的呢。所有的操作都压成这个样子呢?
我在把传进来的数组按照某种逻辑随机呢?
我把数组分类开来呢?
可操作空间是不是太多啦
你懂我的意思把【狗头】