NodeJS生成字节码

NodeJS生成字节码

相关问题:
1.nodejs源码保护
2.nodejs源码加密
3.nodejs提升运行速度

前言

传统的后端运行环境,如 Java、.NET,其源代码是经过编译才部署到服务器上运行的,不存在泄露的风险。而对于应用越来越广泛的 Node.js 而言,运行的则是源代码。即使经过压缩混淆,也可以很大程度地还原。

本文介绍一种可用于 Node.js 端的代码保护方案,使得 Node.js 项目也可以放心地进行私有化部署。

1.原理

当 V8 编译 JavaScript 代码时,解析器将生成一个抽象语法树,进一步生成字节码。Node.js 有一个叫做 vm 的内置模块,创建 vm.Script 的实例时,只要在构造函数中传入 produceCachedData 属性,并设为 true,就可以获取对应代码的字节码。例如:

const vm = require('vm');
const CODE = 'console.log("Hello world");'; // 源代码
const script = new vm.Script(CODE, {
  produceCachedData: true
});
const bytecodeBuffer = script.cachedData; // 字节码

并且,这段字节码可以脱离源代码运行:

const anotherScript = new vm.Script(' '.repeat(CODE.length), {
  cachedData: bytecodeBuffer
});
anotherScript.runInThisContext(); // 'Hello world'

这段代码看起来不那么容易理解,主要体现在创建 vm.Script 实例时传入的第一个参数:

  • 既然源代码的字节码已经在 bytecodeBuffer 中,为何还要传入第一个参数?
  • 为何传入与源代码长度相同的空格?

首先,创建 vm.Script 实例时,V8 会检查字节码(cachedData)是否与源代码(第一个参数传入的代码)匹配,所以第一个参数不能省略。其次,这个检查非常简单,它只会对比代码长度是否一致,所以只要使用与源代码长度相同的空格,就可以“欺骗”这个检查。

字节码计算长度代码如下:

const length = lengthBytes.reduce((sum, number, power) => {
  return sum += number * Math.pow(256, power);
}, 0); // 27

此外,还有一种更简单的方法:

const length = bytecodeBuffer.readIntLE(8, 4); // 27

综上所述,运行字节码的代码可以优化为:

const length = bytecodeBuffer.readIntLE(8, 4);
const anotherScript = new vm.Script(' '.repeat(length), {
  cachedData: bytecodeBuffer
});
anotherScript.runInThisContext();

2.字节码的问题

虽然编译成字节码后可以保护源代码,但字节码也会存在一些问题:

JavaScript 源代码可以在任何平台的 Node.js 环境中运行,但字节码是平台相关的,在何种平台下编译,就只能在何种平台下运行(比如在 Windows 下编译的字节码不能在 macOS 下运行)。

修改源代码后要再次编译为字节码,较为繁琐。对于一些如数据库服务器地址、端口号等配置信息,建议不要编译成字节码,仍使用源文件运行,方便随时修改。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章