时间:2023-07-12 15:24:01 | 来源:网站运营
时间:2023-07-12 15:24:01 来源:网站运营
用虚拟机opcode保护JS源码:JS代码保护,有多种方式,如常规的JS混淆加密、如bytecode化、又或如虚拟机化。push apush bpush ccall funpop
这是古老的asm语法,没错,js代码可以转为此种形式,而且,可以更进一步,转为opcode,如上述asm代码,如果将push、pop等字符替换为数字的操作码,假设push为20,call为30,pop为40,形态可以变成:20,1,20,20,3,30,4,40
如果我们的JS代码,变成了这样的数字,谁能理解它的代码逻辑和作用吗?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}.`); }
三、实例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
看起来仅仅是些数字,先看效果,在虚拟机中执行: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。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代码中,酸爽。关键词:保护,虚拟