如果為了降低部署復雜度,可以考慮使用@vercel/ncc。除非有特別理由,不建議使用SEA。
1.?環境準備
1.1.?基礎要求
- Node.js: >= 19.0.0 (推薦最新LTS版本)
1.2.?安裝依賴
npm install postject typescript
1.3.?驗證環境
node -v # 確認版本 >= 19 tsc -v # 確認TypeScript已安裝 postject --help # 確認命令可用
2.?項目初始化
2.1.?目錄結構
mkdir -p sea-demo/{src,bin,build} cd sea-demo npm init -y
2.2.?示例代碼
創建src/cli.ts:
#!/usr/bin/env nodefunction main() {console.log("Hello from SEA!"); }main();
3.?構建配置
3.1.?配置tsconfig.json
nodejs內置模塊很多還在使用commonjs,統一使用commonjs可以避免很多麻煩。
{"compilerOptions": {"target": "es2020","module": "commonjs","outDir": "bin","esModuleInterop": true,"strict": true} }
3.2.?配置 sea-config.json
{"main": "bin/cli.js","output": "build/sea-prep.blob","disableExperimentalSEAWarning": false,"useSnapshot": false,"useCodeCache": true }
4.?完整構建流程
4.1.?編譯階段
# 編譯TypeScript tsc
4.2.?生成SEA Blob
node --experimental-sea-config sea-config.json
4.3.?準備可執行文件
# 獲取Node路徑 NODE_PATH=$(which node)# 復制并重命名 cp $NODE_PATH build/sea-demo
4.4.?注入Blob
# 獲取當前Node版本的FUSE值 # 有些版本nodejs無法通過下面的命令獲取FUSE值,可以在網上搜索FUSE值。 FUSE_VALUE=$(node -p "process.versions.v8.split('.').slice(0, 3).join('')")# 注入Blob npx postject build/sea-demo NODE_SEA_BLOB build/sea-prep.blob \--sentinel-fuse NODE_SEA_FUSE_${FUSE_VALUE}
4.5.?基礎
./build/sea-demo --version# 查看SEA狀態 strings build/sea-demo | grep NODE_SEA_# 啟用詳細日志 export NODE_DEBUG=sea ./build/sea-demo
5.?高級配置
5.1.?嵌入資源文件
修改sea-config.json:
{"main": "bin/cli.js","output": "build/sea-prep.blob","assets": {"a.txt": "src/assets/a.txt","b.json": "data/config.json"} }
代碼中通過 sea.resources 訪問:
const resources = require('node:sea').resources; console.log(resources.a.txt.toString());
6.?常見問題解決
6.1.?FUSE值不匹配
癥狀: 運行時報?NODE_SEA_FUSE
?錯誤 解決: 重新生成當前Node版本的blob
rm build/sea-prep.blob node --experimental-sea-config sea-config.json
6.2.?模塊加載失敗
癥狀: 出現?Cannot find module
?錯誤 解決: 確保所有依賴已打包:
- 使用?
npm install --save
?安裝依賴 - 在?
sea-config.json
?中添加:
"useNodeBundle": true
7.?附錄: SEA Blob 注入技術詳解
7.1.?基本原理
技術棧組成:
postject
: 基于Electron的跨平臺二進制注入工具FUSE
: 版本特定標識符,防止錯誤注入NODE_SEA_BLOB
: 預定義的二進制段名稱
工作流程:
+---------------------+ +---------------------+ | 原始Node可執行文件 | --> | 查找PE/ELF/MachO的 | +---------------------+ | 特殊可注入區域 |+----------+----------+|+---------------v---------------+| 驗證FUSE標識匹配性 || 檢查段對齊和內存布局 |+---------------+---------------+|+----------v----------+| 寫入壓縮后的JS代碼 || 更新文件校驗和 |+---------------------+
7.2.?關鍵實現細節
二進制格式處理:
- Windows PE: 修改?
.rdata
?段 - Linux ELF: 擴展?
.rodata
?段 - macOS Mach-O: 使用 __LLVM 段
內存布局示例(Linux ELF):
+-------------------------+ | ELF Header | +-------------------------+ | Program Headers | +-------------------------+ | .text | +-------------------------+ | .rodata (原始數據) | +-------------------------+ | NODE_SEA_BLOB (新增) | <-- 注入位置 +-------------------------+ | .data | +-------------------------+
7.3.?數據段保護原理
現代二進制注入工具(如postject)采用以下策略保證安全:
保護機制 | 實現方式 |
---|---|
段末尾注入 | 在目標段的文件空隙區域寫入 |
段擴展 | 通過增加PE/ELF節區大小實現 |
重定位表更新 | 自動調整所有受影響的重定位項 |
校驗和修復 | 更新PE文件的Checksum字段 |
典型二進制文件布局(注入前后對比):
;; 注入前 [.text] [.rodata] [.data] [.imports]... <未使用空間>;; 安全注入后 [.text] [.rodata] [NODE_SEA_BLOB] [.data] [.imports]... ;; 或 [.text] [.rodata+blob] [.data]... (擴展.rodata段)