文档
/
VM 混淆
/

Register-Based VM

Register-Based VM

Pro
v7.12.0+

将 VM 切换为基于寄存器的执行模型,获得更快的运行速度,以及与默认形态不同的 VM 结构。

vmRegisterBased 选项会把 VM 从默认的基于栈的字节码切换为基于寄存器的执行模型。在某些情况下,这能让 VM 运行时性能提升约 10-20%,代价是混淆后的包体略有增大。

启用它会关闭 Stateful Opcodes(vmStatefulOpcodes):两者互不兼容,因此如果原本开启了 vmStatefulOpcodes,基于寄存器的构建会静默地将其去掉。

基于栈与基于寄存器

基于栈的 VM 把操作数保存在一个隐式的栈上:每个值先被压入栈,再被下一条指令取用,结果又被压回栈中。基于寄存器的 VM 则直接寻址操作数,因此同样的工作可以用更少的指令表达,指令之间也没有压栈和出栈的开销。运行时的收益正是来自这里;字节码略微变大的原因也在于此,因为每条指令现在都必须明确写出它读写哪些寄存器,而不是把它们隐含在栈上。

两种模型都不会改变您的代码的行为,也不会改变 VM 所保护的内容。它们是同一段虚拟化逻辑的两种编码方式;基于寄存器只是用一点体积换取一点速度。

何时使用

在 VM 运行时开销很重要时使用它。 虚拟化代码在设计上就比普通 JavaScript 慢,因此在热点路径上(动画循环、每帧处理函数、紧凑的解析例程),基于寄存器的执行器能节省的约 10-20% 值得付出更大的包体。对于很少运行的代码,体积成本通常不划算。收益取决于具体代码,请在自己的热点路径上测量。

它还会改变 VM 的形态。 由于它生成的字节码和执行器在结构上不同,其输出带有与常见的基于栈的 VM 不同的特征。针对这种基于栈的形态调校的通用、基于模式的分析,更难识别基于寄存器的构建,因此这是让交付的 VM 偏离默认形态的一种方法。

内部实现

这并不是一个原生的基于寄存器的编译器。常规的基于栈的编译器仍会像未启用该选项时一样生成字节码;随后由一个单独的转换阶段把该字节码改写为基于寄存器的形式。由于基于寄存器的改写位于它们全部的下游,VM 混淆的大部分内容(函数选择、Bytecode Encoding、调度器选项)都不受影响(Stateful Opcodes 除外,见上文)。

要求

  • 需要 vmObfuscation:此选项只会重塑 vmObfuscation 生成的 VM。
  • 混淆器版本 7.12.0 或更高。

此选项为实验性功能。 基于寄存器的改写比默认的基于栈的路径更新,经过的实战检验也更少,因此在发布之前,请测试您的代码在启用 vmRegisterBased 后能否正确运行。这条建议适用于任何 VM 选项,但对它尤其重要。

示例

JavaScript

基于寄存器的执行可以与 VM 混淆的其他部分组合使用:Bytecode Encoding、Compact Dispatcher、VM Self Defending 和 VM Domain Lock 的工作方式都与默认的基于栈的 VM 完全相同。它改变的是字节码的执行方式,而不是包裹它的保护措施。