聊聊electron代码加密

electron技术就是基于开源的chromium内核,实现了跨端的桌面端开发框架,所以其常用的业务开发脚本就是js代码了

那么在打包后,这些js代码虽然会经过压缩混淆编译等处理,但是仍然可以破解出来

所以就需要一种技术,把js代码转成二进制文件

如果可以的话,还需要对这些文件进行加密,绑定本地机器的mac地址,保证了激活唯一性

接下来,将聊聊怎么做这些事情

bytenode

通过bytenode将所有的js文件编译为字节码

1
2
3
4
5
const bytenode = window.require('bytenode');
await bytenode.compileFile({
filename: "指定js文件路径名称",
output: "输出jsc的目录"
});

webpack插件

用来修改按需加载资源的方式,不再加载js资源了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const pluginName = 'ChangeEntryWebpackPlugin';
const webpack = require('webpack');

const { Template } = webpack;

module.exports = class Plugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
const { mainTemplate } = compilation;
mainTemplate.hooks.requireEnsure.tap(pluginName, (source) => {
return Template.asString([
`if(!window.StartTest){`,
Template.indent([
// ${pluginName} jsc loaded`,
`var installedChunkData = installedChunks[chunkId];`,
`if (installedChunkData !== 0) {`,
Template.indent([
`if (installedChunkData) {`,
Template.indent([`promises.push(installedChunkData[2]);`]),
`} else {`,
Template.indent([
`var src = jsonpScriptSrc(chunkId);`,
`var name = src.replace("./scripts/","").replace(/\.js$/, '');`,
// 这里改为通过require jsc的方式加载每个bundle文件
// 这里的运行时在electron里面
`require("./"+ name + ".jsc");`,
`installedChunks[chunkId] = undefined;`
]),
`}`
]),
`}`,
`return Promise.all(promises);`
]),
`}`,
source
]);
});
});
}
};

步骤

  1. 先将前端单页项目,通过webpack编译后,生成js文件
  2. 启动供编译jsc的electron,在该electron里面,将上一步生成的js文件,编译为jsc的字节码文件。在electron里面编译的目的,是要保证版本一致性,因为该jsc文件,最终也会打包到最后的electron的产物里面
  3. 最后执行需要生成最终包的electron脚本
  4. 运行时加载方式伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 这里使用的是基于award框架的项目示例
import * as path from 'path';

if (document.location.protocol === 'file:') {
if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'uat') {
process.env.NODE_ENV = 'production';

window.onload = function () {
const award = document.getElementById('award');
if (award) {
// 初始化第三方依赖资源
const win = window as any;
win.React = require('react');
win.ReactDOM = require('react-dom');
win.moment = require('moment');
win.antd = require('antd');
win.__INITIAL_STATE__ = { award: {} };

// 加载核心的jsc资源
require('bytenode');
require(path.join(__dirname, 'assets/scripts/common.jsc'));
require(path.join(__dirname, 'assets/scripts/manifest.jsc'));
require(path.join(__dirname, 'assets/scripts/main.jsc'));
}
};
}
}