Loading

手搓 jsvmp

代码

一个支持自定义虚拟机指令集和解释器的简易版 jsvmp

const instructions = [
  { op: "LOAD", arg: Math.random },
  { op: "LOAD", arg: Math },
  { op: "LOAD", arg: 0 },
  { op: "CALL" },
  { op: "RETURN" },
];

class BytecodeGenerator {
  constructor() {
    this.bytecode = [];
    this.constantMap = {};
    this.opcodeMap = {};
  }

  generate(instructions) {
    for (const instr of instructions) {
      const op = instr.op;
      if (!(op in this.opcodeMap)) {
        this.opcodeMap[op] = Object.keys(this.opcodeMap).length + 1;
      }
      this.bytecode.push(this.opcodeMap[instr.op]);
      if (typeof instr.arg === "number") {
        this.bytecode.push(-1);
        this.bytecode.push(instr.arg);
      } else if (instr.arg !== undefined) {
        const k = typeof instr.arg + ":" + instr.arg;
        if (!(k in this.constantMap)) {
          this.constantMap[k] = {
            value: instr.arg,
            index: Object.keys(this.constantMap).length,
          };
        }
        this.bytecode.push(this.constantMap[k].index);
      }
    }

    return [
      this.bytecode,
      Object.entries(this.constantMap)
        .sort((a, b) => a[1].index - b[1].index)
        .map(([k, v]) => v.value),
      this.opcodeMap,
    ];
  }
}

const [bytecode, constants, opcodeMap] = new BytecodeGenerator().generate(
  instructions,
);
console.log(bytecode);
console.log(constants);
console.log(opcodeMap);

class VM {
  constructor(bytecode, constants, opcodeMap) {
    this.bytecode = bytecode;
    this.constants = constants;
    this.opcodeMap = opcodeMap;
  }

  run(...args) {
    this.stack = [];
    this.pc = 0;
    while (this.pc < this.bytecode.length) {
      const opcode = this.bytecode[this.pc++];
      switch (opcode) {
        case this.opcodeMap["LOAD"]:
          const i = this.bytecode[this.pc++];
          if (i >= 0) {
            this.stack.push(this.constants[i]);
          } else {
            this.stack.push(this.bytecode[this.pc++]);
          }
          break;
        case this.opcodeMap["CALL"]:
          const s = this.stack.pop();
          const args = [];
          for (let i = 0; i < s; i++) {
            args.unshift(this.stack.pop());
          }
          const obj = this.stack.pop();
          const func = this.stack.pop();
          this.stack.push(Function.prototype.call.call(func, obj, ...args));
          break;
        case this.opcodeMap["RETURN"]:
          return this.stack.pop();
      }
    }
  }
}

const result = new VM(bytecode, constants, opcodeMap).run();
console.log(result);

输出结果

[
   1, 0, 1, 1, 1,
  -1, 0, 2, 3
]
[ [Function: random], Object [Math] {} ]
{ LOAD: 1, CALL: 2, RETURN: 3 }
0.7665310789085834

增强方案

  • 符号混淆
  • 字符串加密
  • bytecode 加密
  • 字节码自修改
  • GOTO 乱序执行
  • vm in vm

逆向分析方法

  • 线性反汇编
  • 控制流反汇编
  • 插桩打印日志 (trace)

附:
babel ast spec: https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md
ast explorer: https://astexplorer.net/

posted @ 2026-08-07 14:33  xtyuns  阅读(6)  评论(0)    收藏  举报