Electron 源码保护

这是一个示例,演示如何快速在你的 Electron 项目中启用字节码保护,没有多余的废话。

环境

tech version
electron 30.0.6
webpack 5.91.0
@herberttn/bytenode-webpack-plugin 2.3.1
nodejs v20.14.0

步骤

Webpack 配置

参考文档

// 引入依赖
const { BytenodeWebpackPlugin } = require('@herberttn/bytenode-webpack-plugin');
// 在生产环境启用
const isEnvProduction = process.env.NODE_ENV === 'production';
...
plugins: [
  isEnvProduction && new BytenodeWebpackPlugin({ compileForElectron: true }),
],
...
// main,preload,renderer 需要更改 entry 配置。我使用了 webpack-merge,如果你没有使用,忽略即可。
// main
const mainConfig = merge(commonConfig, {
  // entry: './src/main/main.ts',
  entry: {
    main: './src/main/main.ts',
  },
  target: 'electron-main',
  output: {
    filename: '[name].js',
    devtoolModuleFilenameTemplate: '[absolute-resource-path]',
  },
  ...
})
// preload
const preloadConfig = merge(commonConfig, {
    // entry: './src/preload/preload.ts',
    entry: {
      preload: './src/preload/preload.ts',
    },
    target: 'electron-preload',
    output: {
      filename: '[name].js',
      devtoolModuleFilenameTemplate: '[absolute-resource-path]',
    },
});
// renderer
const rendererConfig = merge(commonConfig, {
  entry: {
    renderer: './src/renderer/renderer.tsx',
  },
  target: 'electron-renderer',
  output: { devtoolModuleFilenameTemplate: '[absolute-resource-path]' },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './public/index.html'),
    }),
  ],
});

Electron 入口 main.ts/main.js 配置

  mainWindow = new BrowserWindow({
  ...
  webPreferences: {
    nodeIntegration: true, // 启用 jsc 支持
    contextIsolation: false, // false 是启用 jsc 支持,但是用不了 preload 的 contextBridge
    preload: path.join(__dirname, './preload.js'), // bytenode 编译后生成的 js,用于加载 preload.compiled.jsc
    webSecurity: false,
    sandbox: false,
  },
});

我知道上面的配置,不符合 Electron 的默认安全配置。但是 Electron 的默认安全配置开启后,你基本上什么也做不了。

如果你希望使用字节码,你必须按照以上配置。

preload contextBridge 修复

contextIsolation: false 会导致 contextBridge 无法使用,这又是 Electron 的一个有趣决定之一。

contextBridge 无法使用,那么你就无法在使用 ipc,当然,你也不需要了。

此时你可以在 renderer 中直接操作 node 的 api,如 fs 等。但是我建议还是通过 preload 中转一下,凡事留一线,日后好相见。

- import { contextBridge } from 'electron';
- import ipcAPI from '_preload/ipc-api';
import loadAddon from "_preload/load_node_addon";
import jb from "_preload/nodejieba";

- contextBridge.exposeInMainWorld('ipcAPI', ipcAPI);
- contextBridge.exposeInMainWorld('myplugin', loadAddon('myplugin'));
- contextBridge.exposeInMainWorld('jb', jb);

+ window.ipcAPI = ipcAPI;
+ window.myplugin = loadAddon('myplugin');
+ window.jb = jb;

完成

通过 webpack.js,main.js,preload.js 中的以上三处配置,你应该可以在你的项目中使用字节码了。

字节码仍然不是最终方案,因为字节码很容易被反编译,目前阶段它只是增加了初级破解者的一些成本。

有些人使用了在 rust 中编写本地插件,和字节码搭配混淆的方案,如果你对代码有强烈的保密需求,可以参考,并在可维护性和安全性上取舍。