找回密码
 立即注册
快捷导航

[Html/Css/JS] JS加密?用虚拟机opcode保护JS源码

[复制链接]
admin 2024-3-6 14:00:09 | 显示全部楼层

JS代码保护,有多种方式,如常规的JS混淆加密、如bytecode化、又或如虚拟机化。

这里简单探讨虚拟机JS保护。

一、原理

虚拟机保护的最终目标,是将JS代码转为opcode,或汇编语言式代码,在虚拟机中执行。

一般是保护重要的函数、算法、当然也可以保护更多更大段的代码。

更详细一些来说,汇编语言式代码,形态会类似:

push a
push b
push c
call fun
pop

这是古老的asm语法,没错,js代码可以转为此种形式,而且,可以更进一步,转为opcode,如上述asm代码,如果将push、pop等字符替换为数字的操作码,假设push为20,call为30,pop为40,形态可以变成:

20,1,20,20,3,30,4,40

如果我们的JS代码,变成了这样的数字,谁能理解它的代码逻辑和作用吗?

很显然,这样起到了对代码加密保护的作用。如果再与JShaman之类的混淆加密工具配合使用,JS代码的安全性将得到极大的提升。

二、开发一个JS虚拟机

一个简单的堆栈虚拟机,并不会十分复杂,用JS数组模拟堆栈,用数组的push方法模拟压栈,用数组索实现堆栈指针、指令指针、栈帧。

本例中,汇编指令,则实现一部分操作,如:

const I = {
  CONST: 1,
  ADD: 2,
  PRINT: 3,
  HALT: 4,
  CALL: 5,
  RETURN: 6,
  LOAD: 7,
  JUMP_IF_ZERO: 8,
  JUMP_IF_NOT_ZERO: 9,
  SUB: 10,
  MUL: 11,
};

虚拟机的核心的部分,则是根据指令进行相应的堆栈操作,如:

//循环执行
  switch (instruction) {
    //常量
    case I.CONST: {
      //常量值
      const op_value = code[ip++];
      //存放到堆栈
      stack[++sp] = op_value;

      console.log("const",stack)
      break;
    }
    case I.ADD: {
      const op1 = stack[sp--];
      const op2 = stack[sp--];
      stack[++sp] = op1 + op2;
      break;
    }
    //减法
    case I.SUB: {
      //减数
      const op1 = stack[sp--];
      //被减数,都放在堆栈里
      const op2 = stack[sp--];
      //相减的结果,放到堆栈
      stack[++sp] = op2 - op1;

      break;
    }
    case I.PRINT: {
      const value = stack[sp--];
      builtins.print(value);
      break;
    }
    case I.HALT: {
      return;
    }
    //函数调用
    case I.CALL: {
      //函数地址
      const op1_address = code[ip++];
      //参数个数
      const op2_numberOfArguments = code[ip++];
      console.log(".....",op1_address,op2_numberOfArguments)

      //参数个数入栈
      stack[++sp] = op2_numberOfArguments;

      //旧栈帧入栈
      stack[++sp] = fp;
      //指令指针
      stack[++sp] = ip;

      //console.log("call",stack);return

      //独立的栈帧,从当前堆栈指针处开始
      fp = sp;
      //指令指针变化,开始执行call函数
      ip = op1_address;
      break;
    }
    case I.RETURN: {
      const returnValue = stack[sp--];
      sp = fp;

      ip = stack[sp--];
      fp = stack[sp--];
      const number_of_arguments = stack[sp--];

      sp -= number_of_arguments;
      stack[++sp] = returnValue;
      break;
    }
    case I.LOAD: {
      //补偿地址,ip指向指令地址,通过补偿值,获得函数调用前压入的参数
      const op_offset = code[ip++];

      const value = stack[fp + op_offset];
      //console.log(value);return

      stack[++sp] = value;
      break;
    }
    case I.JUMP_IF_NOT_ZERO: {
      const op_address = code[ip++];
      const value = stack[sp--];
      if (value !== 0) {
        ip = op_address;
      }
      break;
    }
    default:

      throw new Error(`Unknown instruction: ${instruction}.`);
  }

三、实例

JS虚拟机已简单实现。然后,准备一段JS代码生成的opcode,如下:

1, 10,  5, 7,  1,  3, 4, 7, -3,1,  1, 10, 9, 17,  1, 1, 6,  7, -3,  7, -3, 1,  1, 10, 5, 7,  1,  11,  6

看起来仅仅是些数字,先看效果,在虚拟机中执行:

JS加密?用虚拟机opcode保护JS源码7174 作者:admin 帖子ID:994

如上图,输出是一个数值。那么,这段opcode究竟是什么呢?

其实,它是这样一段JS源代码转化而来:

function factorial(n) {
  if (n === 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

const result = factorial(10);
console.log(result);

将上述opcode转换一个形式,把数字替换为前面讲到过的汇编指令,会得到如下形式的类asm代码:

I.CONST,
  10,
  I.CALL,
  /* factorial */ 7,
  1,
  I.PRINT,
  I.HALT,
  I.LOAD, // factorial start,7指向的即是这里
  -3,
  I.CONST,
  1,
  I.SUB,
  I.JUMP_IF_NOT_ZERO,
  17,
  I.CONST,
  1,
  I.RETURN,
  /* n */ I.LOAD,
  -3,
  /* factorial(n - 1) */ I.LOAD,
  -3,
  I.CONST,
  1,
  I.SUB,
  I.CALL,
  /* factorial */ 7,
  1,
  I.MUL,
  I.RETURN, // factorial end

对照JS源码、虚拟机代码,仔细阅读,方能理解此段汇编代码的含意,相应的,也就可以理解opcode。

但如果未得到得虚拟机代码,或是虚拟机代码又被进行了加密,如:使用JShaman对虚拟机代码进行了混淆加密。那,想要理解opcode,则是万难。

最后,请再来欣赏这段优雅的JS代码:

1, 10,  5, 7,  1,  3, 4, 7, -3,1,  1, 10, 9, 17,  1, 1, 6,  7, -3,  7, -3, 1,  1, 10, 5, 7,  1,  11,  6

仅是一行,如果是大段大段的,或是夹杂在混淆加密保护过的JS代码中,酸爽。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

温馨提示

关于 注册码 问题

      由于近期经常大量注册机器人注册发送大量广告,本站开启免费入群领取注册码注册网站账号,注册码在群公告上贴着...

关于 注册码 问题

      由于近期经常大量注册机器人注册发送大量广告,本站开启免费入群领取注册码注册网站账号,注册码在群公告上贴着...

Archiver|手机版|小黑屋|DLSite

GMT+8, 2024-11-21 19:50

Powered by Discuz! X3.5 and PHP8

快速回复 返回顶部 返回列表