-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpatch.js
193 lines (187 loc) · 5.81 KB
/
patch.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
const path = require('path');
const fs = require('fs');
const PatchedSentinel = 'sLw2p0mwn1P3QsLwGbe';
const FuseConst = {
Sentinel: 'dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX',
ExpectedFuseVersion: 1,
EnabledByte: 0x31,
DisabledByte: 0x30,
RemovedByte: 0x72,
RunAsNode: 1
};
const replacements = [
{
name: 'Command-line option: --inspect',
search: /\0--inspect\0/g,
replace: '\0 inspect\0'
},
{
name: 'Command-line option: --inspect-brk',
search: /\0--inspect-brk\0/g,
replace: '\0 inspect-brk\0'
},
{
name: 'Command-line option: --inspect-port',
search: /\0--inspect-port\0/g,
replace: '\0 inspect-port\0'
},
{
name: 'Command-line option: --debug',
search: /\0--debug\0/g,
replace: '\0 debug\0'
},
{
name: 'Command-line option: --debug-brk',
search: /\0--debug-brk\0/g,
replace: '\0 debug-brk\0'
},
{
name: 'Command-line option: --debug-port',
search: /\0--debug-port\0/g,
replace: '\0 debug-port\0'
},
{
name: 'Command-line option: --inspect-brk-node',
search: /\0--inspect-brk-node\0/g,
replace: '\0 inspect-brk-node\0'
},
{
name: 'Command-line option: --inspect-publish-uid',
search: /\0--inspect-publish-uid\0/g,
replace: `\0 ${PatchedSentinel}\0`
},
{
name: 'Electron option: javascript-harmony',
search: /\0javascript-harmony\0/g,
replace: '\0xx\r\n \0\0\0\0\0\0\0\0\0\0\0\0\0\0'
},
{
name: 'Electron option: js-flags',
search: /\0js-flags\0/g,
replace: '\0xx\r\n \0\0\0\0'
},
{
name: 'Electron option: remote-debugging-pipe',
search: /\0remote-debugging-pipe\0/g,
replace: '\0xx\r\n \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
},
{
name: 'Electron option: remote-debugging-port',
search: /\0remote-debugging-port\0/g,
replace: '\0xx\r\n \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
},
{
name: 'Electron option: wait-for-debugger-children',
search: /\0wait-for-debugger-children\0/g,
replace: '\0xx\r\n \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
},
{
name: 'DevTools listening message',
search: /\0\nDevTools listening on ws:\/\/%s%s\n\0/g,
replace: '\0%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\0'
},
{
name: 'Debugger listening message',
search: /\0Debugger listening on %s\n\0/g,
replace: '\0%s%s%s%s%s%s%s%s%s%s%s%s\n\0'
}
];
function patch(options) {
const { verbose } = options;
const binary = findBinary(options);
if (!fs.existsSync(binary)) {
throw new Error(`Binary not found: ${binary}`);
}
if (verbose) {
console.log(`Found binary: ${binary}`);
}
let data = fs.readFileSync(binary, 'latin1');
if (verbose) {
console.log(`Read: ${data.length} bytes`);
}
if (data.includes(PatchedSentinel)) {
if (verbose) {
console.log(`Already patched`);
}
return;
}
const [, electronVersion] = data.match(/Electron\/(\d+\.\d+\.\d+)/);
if (verbose) {
console.log(`Found version: ${electronVersion}`);
}
if (electronVersion.split('.')[0] < 12) {
throw new Error(`Minimal supported Electron version is 12, found ${electronVersion}`);
}
data = setFuseWireStatus(data, FuseConst.RunAsNode, false);
if (verbose) {
console.log(`Fuse wire status set`);
}
for (const replacement of replacements) {
let replaced = false;
data = data.replace(replacement.search, (match) => {
if (replaced) {
throw new Error(`Multiple matches found for ${replacement.name}`);
}
if (replacement.replace.length !== match.length) {
throw new Error(
`Length mismatch for ${replacement.name}: ` +
`${replacement.replace.length} <> ${match.length}`
);
}
replaced = true;
return replacement.replace;
});
if (!replaced) {
throw new Error(`Not found: ${replacement.name}`);
}
if (verbose) {
console.log(`Replaced: ${replacement.name}`);
}
}
if (verbose) {
console.log(`Writing output file...`);
}
fs.writeFileSync(binary, Buffer.from(data, 'latin1'));
if (verbose) {
console.log(`Done`);
}
}
function findBinary(options) {
if (options.path.endsWith('.app')) {
return path.join(
options.path,
'Contents',
'Frameworks',
'Electron Framework.framework',
'Versions',
'A',
'Electron Framework'
);
}
return options.path;
}
function setFuseWireStatus(data, wireId, enabled) {
let ix = data.indexOf(FuseConst.Sentinel);
if (ix === -1) {
throw new Error('Fuse sentinel not found');
}
ix += FuseConst.Sentinel.length;
const foundVersion = data.charCodeAt(ix);
if (foundVersion !== FuseConst.ExpectedFuseVersion) {
throw new Error(
`Bad fuse version: ${foundVersion}, expected ${FuseConst.ExpectedFuseVersion}`
);
}
const fuseLength = data.charCodeAt(++ix);
if (fuseLength < wireId) {
throw new Error(`Fuse is too short: ${fuseLength} bytes, expected at least ${wireId}`);
}
let wireByte = data.charCodeAt(ix + wireId);
if (wireByte === FuseConst.RemovedByte) {
throw new Error(`Fuse wire ${wireId} is marked as removed`);
}
wireByte = String.fromCharCode(enabled ? FuseConst.EnabledByte : FuseConst.DisabledByte);
data = data.substr(0, ix + wireId) + wireByte + data.substr(ix + wireId + 1);
return data;
}
module.exports = patch;