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 下運行)。

修改源代碼後要再次編譯爲字節碼,較爲繁瑣。對於一些如數據庫服務器地址、端口號等配置信息,建議不要編譯成字節碼,仍使用源文件運行,方便隨時修改。

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