Improved executable binary handling
Ylian Saint-Hilaire committed
Jan 19, 2018 at 18:04 UTC
65f99a3c31e6f877e0598a4d8b0b593847e3e5a7
11 files changed
+410
-64
MeshCentralServer.njsproj
+1
@@ -28,6 +28,7 @@
28
<Compile Include="amtevents.js" />
29
<Compile Include="amtscanner.js" />
30
<Compile Include="amtscript.js" />
31
+ <Compile Include="exeHandler.js" />
32
<Compile Include="letsencrypt.js" />
33
<Compile Include="meshaccelerator.js" />
34
<Compile Include="meshmail.js" />
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_pi
Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ
agents/meshcmd.js
+6
-7
@@ -279,12 +279,12 @@ function startLms() {
279
//console.log("WebSocket for " + req.url.split('?')[0]);
280
switch (req.url.split('?')[0]) {
281
case '/lms.ashx': // MeshCommander control channel (PTHI)
282
- socket.upgradeWebSocket();
283
- socket.on('data', processLmsControlData);
282
+ socket.ws = socket.upgradeWebSocket();
283
+ socket.ws.on('data', processLmsControlData);
284
break;
285
case '/webrelay.ashx': // MeshCommander data channel (LME)
286
- socket.upgradeWebSocket();
287
- amtLms.bindDuplexStream(socket, 'IPv4', 16992);
286
+ socket.ws = socket.upgradeWebSocket();
287
+ amtLms.bindDuplexStream(socket.ws, 'IPv4', 16992);
288
break;
289
default:
290
socket.end();
@@ -295,9 +295,8 @@ function startLms() {
295
//console.log("WebRequest for " + req.url.split('?')[0]);
296
switch (req.url.split('?')[0]) {
297
case '/': // Serve MeshCommander Web Application for LMS
298
- rsp.writeHead(200, 'OK', { Server: 'JSLMS', 'Cache-Control': 'max-age=0, no-cache', 'X-Frame-Options': 'DENY', 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', ETag: _IntelAmtWebApp_etag });
299
- rsp.write(Buffer.from(_IntelAmtWebApp, 'base64'));
300
- rsp.end();
298
+ rsp.writeHead(200, 'OK', { Server: 'JSLMS', 'Cache-Control': 'max-age=0, no-cache', 'X-Frame-Options': 'DENY', 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', 'Transfer-Encoding': 'chunked', ETag: _IntelAmtWebApp_etag });
299
+ rsp.end(Buffer.from(_IntelAmtWebApp, 'base64'));
300
break;
301
default: // Unknown request
302
rsp.statusCode = 404;
agents/meshcore.js
+2
-2
@@ -692,7 +692,7 @@ function createMeshCore(agent) {
692
// Called when receiving control data on websocket
693
function onTunnelControlData(data) {
694
if (typeof data != 'string') return;
695
- //sendConsoleText('onTunnelControlData: ' + data);
695
+ sendConsoleText('onTunnelControlData: ' + data);
696
//console.log('onTunnelControlData: ' + data);
697
698
var obj;
@@ -729,7 +729,7 @@ function createMeshCore(agent) {
729
this.webrtc.on('connected', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected'); });
730
this.webrtc.on('disconnected', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected'); });
731
this.webrtc.on('dataChannel', function (rtcchannel) {
732
- //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
732
+ sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
733
rtcchannel.xrtc = this;
734
this.rtcchannel = rtcchannel;
735
this.rtcchannel.on('data', onTunnelWebRTCControlData);
exeHandler.js
new
+308
@@ -0,0 +1,308 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
+const exeJavaScriptGuid = 'B996015880544A19B7F7E9BE44914C18';
18
+const exeMeshPolicyGuid = 'B996015880544A19B7F7E9BE44914C19';
19
+
20
+
21
+// Changes a Windows Executable to add JavaScript inside of it.
22
+// This method will write to destination stream and close it.
23
+//
24
+// options = {
25
+// platform: 'win32' or 'linux',
26
+// sourceFileName: 'pathToBinary',
27
+// destinationStream: 'outputStream'
28
+// js: 'jsContent',
29
+// peinfo {} // Optional, if PE header already parsed place it here.
30
+// }
31
+//
32
+module.exports.streamExeWithJavaScript = function (options) {
33
+ // Check all inputs
34
+ if (!options.platform) { throw ('platform not specified'); }
35
+ if (!options.destinationStream) { throw ('destination stream was not specified'); }
36
+ if (!options.sourceFileName) { throw ('source file not specified'); }
37
+ if (!options.js) { throw ('js content not specified'); }
38
+
39
+ // If a Windows binary, parse it if not already parsed
40
+ if ((options.platform == 'win32') && (!options.peinfo)) { options.peinfo = require('PE_Parser')(options.sourcePath); }
41
+
42
+ // If unsigned Windows or Linux, we merge at the end with the GUID and no padding.
43
+ if ((options.platform == 'win32' && options.peinfo.CertificateTableAddress == 0) || options.platform != 'win32') {
44
+ // This is not a signed binary, so we can just send over the EXE then the MSH
45
+ options.destinationStream.sourceStream = require('fs').createReadStream(options.sourceFileName, { flags: 'r' });
46
+ options.destinationStream.sourceStream.options = options;
47
+ options.destinationStream.sourceStream.on('end', function () {
48
+ // Once the binary is streamed, write the msh + length + guid in that order.
49
+ this.options.destinationStream.write(this.options.js); // JS content
50
+ var sz = Buffer.alloc(4);
51
+ sz.writeUInt32BE(this.options.js.length, 0);
52
+ this.options.destinationStream.write(sz); // Length in small endian
53
+ this.options.destinationStream.end(Buffer.from(exeJavaScriptGuid, 'hex')); // GUID
54
+ });
55
+ // Pipe the entire source binary without ending the stream.
56
+ options.destinationStream.sourceStream.pipe(options.destinationStream, { end: false });
57
+ } else {
58
+ throw ('js content not specified');
59
+ }
60
+}
61
+
62
+
63
+// Changes a Windows Executable to add the MSH inside of it.
64
+// This method will write to destination stream and close it.
65
+//
66
+// options = {
67
+// platform: 'win32' or 'linux',
68
+// sourceFileName: 'pathToBinary',
69
+// destinationStream: 'outputStream'
70
+// msh: 'mshContent',
71
+// peinfo {} // Optional, if PE header already parsed place it here.
72
+// }
73
+//
74
+module.exports.streamExeWithMeshPolicy = function (options) {
75
+ // Check all inputs
76
+ if (!options.platform) { throw ('platform not specified'); }
77
+ if (!options.destinationStream) { throw ('destination stream was not specified'); }
78
+ if (!options.sourceFileName) { throw ('source file not specified'); }
79
+ if (!options.msh) { throw ('msh content not specified'); }
80
+
81
+ // If a Windows binary, parse it if not already parsed
82
+ if ((options.platform == 'win32') && (!options.peinfo)) { options.peinfo = require('PE_Parser')(options.sourcePath); }
83
+
84
+ // If unsigned Windows or Linux, we merge at the end with the GUID and no padding.
85
+ if ((options.platform == 'win32' && options.peinfo.CertificateTableAddress == 0) || options.platform != 'win32') {
86
+ // This is not a signed binary, so we can just send over the EXE then the MSH
87
+ options.destinationStream.sourceStream = require('fs').createReadStream(options.sourceFileName, { flags: 'r' });
88
+ options.destinationStream.sourceStream.options = options;
89
+ options.destinationStream.sourceStream.on('end', function () {
90
+ // Once the binary is streamed, write the msh + length + guid in that order.
91
+ this.options.destinationStream.write(this.options.msh); // MSH
92
+ var sz = Buffer.alloc(4);
93
+ sz.writeUInt32BE(this.options.msh.length, 0);
94
+ this.options.destinationStream.write(sz); // Length in small endian
95
+ this.options.destinationStream.end(Buffer.from(exeMeshPolicyGuid, 'hex')); // Guid
96
+ });
97
+ // Pipe the entire source binary without ending the stream.
98
+ options.destinationStream.sourceStream.pipe(options.destinationStream, { end: false });
99
+ } else if (options.platform == 'win32' && options.peinfo.CertificateTableAddress != 0) {
100
+ // This is a signed windows binary, so we need to do some magic
101
+ options.mshPadding = (8 - ((options.peinfo.certificateDwLength + options.msh.length + 20) % 8)) % 8; // Compute the padding with quad-align
102
+
103
+ //console.log('old table size = ' + options.peinfo.CertificateTableSize);
104
+ options.peinfo.CertificateTableSize += (options.msh.length + 20 + options.mshPadding); // Add to the certificate table size
105
+ //console.log('new table size = ' + options.peinfo.CertificateTableSize);
106
+ //console.log('old certificate dwLength = ' + options.peinfo.certificateDwLength);
107
+ options.peinfo.certificateDwLength += (options.msh.length + 20 + options.mshPadding); // Add to the certificate size
108
+ //console.log('new certificate dwLength = ' + options.peinfo.certificateDwLength);
109
+ //console.log('values were padded with ' + options.mshPadding + ' bytes');
110
+
111
+ // Read up to the certificate table size and stream that out
112
+ options.destinationStream.sourceStream = require('fs').createReadStream(options.sourceFileName, { flags: 'r', start: 0, end: options.peinfo.CertificateTableSizePos - 1 });
113
+ options.destinationStream.sourceStream.options = options;
114
+ options.destinationStream.sourceStream.on('end', function () {
115
+ // We sent up to the CertificateTableSize, now we need to send the updated certificate table size
116
+ //console.log('read first block');
117
+ var sz = Buffer.alloc(4);
118
+ sz.writeUInt32LE(this.options.peinfo.CertificateTableSize, 0);
119
+ this.options.destinationStream.write(sz); // New cert table size
120
+
121
+ // Stream everything up to the start of the certificate table entry
122
+ var source2 = require('fs').createReadStream(options.sourceFileName, { flags: 'r', start: this.options.peinfo.CertificateTableSizePos + 4, end: this.options.peinfo.CertificateTableAddress - 1 });
123
+ source2.options = this.options;
124
+ source2.on('end', function () {
125
+ // We've sent up to the Certificate DWLength, which we need to update
126
+ //console.log('read second block');
127
+ var sz = Buffer.alloc(4);
128
+ sz.writeUInt32LE(this.options.peinfo.certificateDwLength, 0);
129
+ this.options.destinationStream.write(sz); // New certificate length
130
+
131
+ // Stream the entire binary until the end
132
+ var source3 = require('fs').createReadStream(options.sourceFileName, { flags: 'r', start: this.options.peinfo.CertificateTableAddress + 4 });
133
+ source3.options = this.options;
134
+ source3.on('end', function () {
135
+ // We've sent the entire binary... Now send: Padding + MSH + MSHLength + GUID
136
+ //console.log('read third block');
137
+ if (this.options.mshPadding > 0) { this.options.destinationStream.write(Buffer.alloc(this.options.mshPadding)); } // Padding
138
+
139
+ this.options.destinationStream.write(this.options.msh); // MSH content
140
+ var sz = Buffer.alloc(4);
141
+ sz.writeUInt32BE(this.options.msh.length, 0);
142
+ this.options.destinationStream.write(sz); // MSH Length, small-endian
143
+ this.options.destinationStream.end(Buffer.from(exeMeshPolicyGuid, 'hex')); // MSH GUID
144
+ });
145
+ source3.pipe(this.options.destinationStream, { end: false });
146
+ this.options.sourceStream = source3;
147
+ });
148
+ source2.pipe(this.options.destinationStream, { end: false });
149
+ this.options.destinationStream.sourceStream = source2;
150
+ });
151
+ this.options.destinationStream.sourceStream.pipe(this.options.destinationStream, { end: false });
152
+ }
153
+}
154
+
155
+
156
+// Return information about this executable
157
+// This works only on Windows binaries
158
+module.exports.parseWindowsExecutable = function (exePath) {
159
+ var retVal = {};
160
+ var fs = require('fs');
161
+ var fd = fs.openSync(exePath, 'r');
162
+ var bytesRead;
163
+ var dosHeader = Buffer.alloc(64);
164
+ var ntHeader = Buffer.alloc(24);
165
+ var optHeader;
166
+
167
+ // Read the DOS header
168
+ bytesRead = fs.readSync(fd, dosHeader, 0, 64, 0);
169
+ if (dosHeader.readUInt16LE(0).toString(16).toUpperCase() != '5A4D') { throw ('unrecognized binary format'); }
170
+
171
+ // Read the NT header
172
+ bytesRead = fs.readSync(fd, ntHeader, 0, ntHeader.length, dosHeader.readUInt32LE(60));
173
+ if (ntHeader.slice(0, 4).toString('hex') != '50450000') {
174
+ throw ('not a PE file');
175
+ }
176
+ switch (ntHeader.readUInt16LE(4).toString(16)) {
177
+ case '14c': // 32 bit
178
+ retVal.format = 'x86';
179
+ break;
180
+ case '8664': // 64 bit
181
+ retVal.format = 'x64';
182
+ break;
183
+ default: // Unknown
184
+ retVal.format = undefined;
185
+ break;
186
+ }
187
+
188
+ retVal.optionalHeaderSize = ntHeader.readUInt16LE(20);
189
+ retVal.optionalHeaderSizeAddress = dosHeader.readUInt32LE(60) + 20;
190
+
191
+ // Read the optional header
192
+ optHeader = Buffer.alloc(ntHeader.readUInt16LE(20));
193
+ bytesRead = fs.readSync(fd, optHeader, 0, optHeader.length, dosHeader.readUInt32LE(60) + 24);
194
+ var numRVA = undefined;
195
+
196
+ retVal.CheckSumPos = dosHeader.readUInt32LE(60) + 24 + 64;
197
+ retVal.SizeOfCode = optHeader.readUInt32LE(4);
198
+ retVal.SizeOfInitializedData = optHeader.readUInt32LE(8);
199
+ retVal.SizeOfUnInitializedData = optHeader.readUInt32LE(12);
200
+
201
+ switch (optHeader.readUInt16LE(0).toString(16).toUpperCase()) {
202
+ case '10B': // 32 bit binary
203
+ numRVA = optHeader.readUInt32LE(92);
204
+ retVal.CertificateTableAddress = optHeader.readUInt32LE(128);
205
+ retVal.CertificateTableSize = optHeader.readUInt32LE(132);
206
+ retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 132;
207
+ retVal.rvaStartAddress = dosHeader.readUInt32LE(60) + 24 + 96;
208
+ break;
209
+ case '20B': // 64 bit binary
210
+ numRVA = optHeader.readUInt32LE(108);
211
+ retVal.CertificateTableAddress = optHeader.readUInt32LE(144);
212
+ retVal.CertificateTableSize = optHeader.readUInt32LE(148);
213
+ retVal.CertificateTableSizePos = dosHeader.readUInt32LE(60) + 24 + 148;
214
+ retVal.rvaStartAddress = dosHeader.readUInt32LE(60) + 24 + 112;
215
+ break;
216
+ default:
217
+ throw ('Unknown Value found for Optional Magic: ' + ntHeader.readUInt16LE(24).toString(16).toUpperCase());
218
+ }
219
+ retVal.rvaCount = numRVA;
220
+
221
+ if (retVal.CertificateTableAddress) {
222
+ // Read the authenticode certificate, only one cert (only the first entry)
223
+ var hdr = Buffer.alloc(8);
224
+ fs.readSync(fd, hdr, 0, hdr.length, retVal.CertificateTableAddress);
225
+ retVal.certificate = Buffer.alloc(hdr.readUInt32LE(0));
226
+ fs.readSync(fd, retVal.certificate, 0, retVal.certificate.length, retVal.CertificateTableAddress + hdr.length);
227
+ retVal.certificate = retVal.certificate.toString('base64');
228
+ retVal.certificateDwLength = hdr.readUInt32LE(0);
229
+ }
230
+ fs.closeSync(fd);
231
+ return (retVal);
232
+}
233
+
234
+
235
+//
236
+// Hash a executable file. Works on both Windows and Linux.
237
+// On Windows, will hash so that signature or .msh addition will not change the hash. Adding a .js on un-signed executable will change the hash.
238
+//
239
+// options = {
240
+// sourcePath: <string> Executable Path
241
+// targetStream: <stream.writeable> Hashing Stream
242
+// platform: <string> Optional. Same value as process.platform ('win32' | 'linux' | 'darwin')
243
+// }
244
+//
245
+module.exports.hashExecutableFile = function (options) {
246
+ if (!options.sourcePath || !options.targetStream) { throw ('Please specify sourcePath and targetStream'); }
247
+ var fs = require('fs');
248
+
249
+ // If not specified, try to determine platform type
250
+ if (!options.platform) {
251
+ try {
252
+ // If we can parse the executable, we know it's windows.
253
+ options.peinfo = module.exports.parseWindowsExecutable(options.sourcePath);
254
+ options.platform = 'win32';
255
+ } catch (e) {
256
+ options.platform = 'other';
257
+ }
258
+ }
259
+
260
+ // Setup initial state
261
+ options.state = { endIndex: 0, checkSumIndex: 0, tableIndex: 0, stats: fs.statSync(options.sourcePath) };
262
+
263
+ if (options.platform == 'win32')
264
+ {
265
+ if (options.peinfo.CertificateTableAddress != 0) { options.state.endIndex = options.peinfo.CertificateTableAddress; }
266
+ options.state.tableIndex = options.peinfo.CertificateTableSizePos - 4;
267
+ options.state.checkSumIndex = options.peinfo.CheckSumPos;
268
+ }
269
+
270
+ if (options.state.endIndex == 0) {
271
+ // We just need to check for Embedded MSH file
272
+ var fd = fs.openSync(options.sourcePath, 'r');
273
+ var guid = Buffer.alloc(16);
274
+ var bytesRead;
275
+
276
+ bytesRead = fs.readSync(fd, guid, 0, guid.length, options.state.stats.size - 16);
277
+ if (guid.toString('hex') == exeMeshPolicyGuid) {
278
+ bytesRead = fs.readSync(fd, guid, 0, 4, options.state.stats.size - 20);
279
+ options.state.endIndex = options.state.stats.size - 20 - guid.readUInt32LE(0);
280
+ } else {
281
+ options.state.endIndex = options.state.stats.size;
282
+ }
283
+ fs.closeSync(fd);
284
+ }
285
+
286
+ // Linux does not have a checksum
287
+ if (options.state.checkSumIndex != 0) {
288
+ // Windows
289
+ options.state.source = fs.createReadStream(options.sourcePath, { flags: 'r', start: 0, end: options.state.checkSumIndex - 1 });
290
+ options.state.source.on('end', function () {
291
+ options.targetStream.write(Buffer.alloc(4));
292
+ var source = fs.createReadStream(options.sourcePath, { flags: 'r', start: options.state.checkSumIndex + 4, end: options.state.tableIndex - 1 });
293
+ source.on('end', function () {
294
+ options.targetStream.write(Buffer.alloc(8));
295
+ var source = fs.createReadStream(options.sourcePath, { flags: 'r', start: options.state.tableIndex + 8, end: options.state.endIndex - 1 });
296
+ options.state.source = source;
297
+ options.state.source.pipe(options.targetStream);
298
+ });
299
+ options.state.source = source;
300
+ options.state.source.pipe(options.targetStream, { end: false });
301
+ });
302
+ options.state.source.pipe(options.targetStream, { end: false });
303
+ } else {
304
+ // Linux
305
+ options.state.source = fs.createReadStream(options.sourcePath, { flags: 'r', start: 0, end: options.state.endIndex - 1 });
306
+ options.state.source.pipe(options.targetStream);
307
+ }
308
+}
meshcentral.js
+51
-49
@@ -25,6 +25,7 @@ function CreateMeshCentralServer() {
25
obj.fs = require('fs');
26
obj.path = require('path');
27
obj.crypto = require('crypto');
28
+ obj.exeHandler = require('./exeHandler.js');
29
obj.platform = require('os').platform();
30
obj.args = require('minimist')(process.argv.slice(2));
31
obj.common = require('./common.js');
@@ -854,64 +855,65 @@ function CreateMeshCentralServer() {
855
856
// List of possible mesh agents
857
obj.meshAgentsArchitectureNumbers = {
857
- 0: { id: 0, localname: 'Unknown', rname: 'meshconsole.exe', desc: 'Unknown agent', update: false, amt: true },
858
- 1: { id: 1, localname: 'MeshConsole.exe', rname: 'meshconsole.exe', desc: 'Windows x86-32 console', update: true, amt: true },
859
- 2: { id: 2, localname: 'MeshConsole64.exe', rname: 'meshconsole.exe', desc: 'Windows x86-64 console', update: true, amt: true },
860
- 3: { id: 3, localname: 'MeshService.exe', rname: 'meshagent.exe', desc: 'Windows x86-32 service', update: true, amt: true },
861
- 4: { id: 4, localname: 'MeshService64.exe', rname: 'meshagent.exe', desc: 'Windows x86-64 service', update: true, amt: true },
862
- 5: { id: 5, localname: 'meshagent_x86', rname: 'meshagent', desc: 'Linux x86-32', update: true, amt: true },
863
- 6: { id: 6, localname: 'meshagent_x86-64', rname: 'meshagent', desc: 'Linux x86-64', update: true, amt: true },
864
- 7: { id: 7, localname: 'meshagent_mips', rname: 'meshagent', desc: 'Linux MIPS', update: true, amt: false },
865
- 8: { id: 8, localname: 'MeshAgent-Linux-XEN-x86-32', rname: 'meshagent', desc: 'XEN x86-64', update: true, amt: false },
866
- 9: { id: 9, localname: 'meshagent_arm', rname: 'meshagent', desc: 'Linux ARM5', update: true, amt: false },
867
- 10: { id: 10, localname: 'MeshAgent-Linux-ARM-PlugPC', rname: 'meshagent', desc: 'Linux ARM PlugPC', update: true, amt: false },
868
- 11: { id: 11, localname: 'MeshAgent-OSX-x86-32', rname: 'meshosx', desc: 'Apple OSX x86-32', update: true, amt: false },
869
- 12: { id: 12, localname: 'MeshAgent-Android-x86', rname: 'meshandroid', desc: 'Android x86-32', update: true, amt: false },
870
- 13: { id: 13, localname: 'meshagent_pogo', rname: 'meshagent', desc: 'Linux ARM PogoPlug', update: true, amt: false },
871
- 14: { id: 14, localname: 'MeshAgent-Android-APK', rname: 'meshandroid', desc: 'Android Market', update: false, amt: false }, // Get this one from Google Play
872
- 15: { id: 15, localname: 'meshagent_poky', rname: 'meshagent', desc: 'Linux Poky x86-32', update: true, amt: false },
873
- 16: { id: 16, localname: 'MeshAgent-OSX-x86-64', rname: 'meshagent', desc: 'Apple OSX x86-64', update: true, amt: false },
874
- 17: { id: 17, localname: 'MeshAgent-ChromeOS', rname: 'meshagent', desc: 'Google ChromeOS', update: false, amt: false }, // Get this one from Chrome store
875
- 18: { id: 18, localname: 'meshagent_poky64', rname: 'meshagent', desc: 'Linux Poky x86-64', update: true, amt: false },
876
- 19: { id: 19, localname: 'meshagent_x86_nokvm', rname: 'meshagent', desc: 'Linux x86-32 NoKVM', update: true, amt: true },
877
- 20: { id: 20, localname: 'meshagent_x86-64_nokvm', rname: 'meshagent', desc: 'Linux x86-64 NoKVM', update: true, amt: true },
878
- 21: { id: 21, localname: 'MeshAgent-WinMinCore-Console-x86-32.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Console x86-32', update: true, amt: false },
879
- 22: { id: 22, localname: 'MeshAgent-WinMinCore-Service-x86-64.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Service x86-32', update: true, amt: false },
880
- 23: { id: 23, localname: 'MeshAgent-NodeJS', rname: 'meshagent', desc: 'NodeJS', update: false, amt: false }, // Get this one from NPM
881
- 24: { id: 24, localname: 'meshagent_arm-linaro', rname: 'meshagent', desc: 'Linux ARM Linaro', update: true, amt: false },
882
- 25: { id: 25, localname: 'meshagent_pi', rname: 'meshagent', desc: 'Linux ARM - Raspberry Pi', update: true, amt: false } // "armv6l" and "armv7l"
858
+ 0: { id: 0, localname: 'Unknown', rname: 'meshconsole.exe', desc: 'Unknown agent', update: false, amt: true, platform: 'unknown' },
859
+ 1: { id: 1, localname: 'MeshConsole.exe', rname: 'meshconsole.exe', desc: 'Windows x86-32 console', update: true, amt: true, platform: 'win32' },
860
+ 2: { id: 2, localname: 'MeshConsole64.exe', rname: 'meshconsole.exe', desc: 'Windows x86-64 console', update: true, amt: true, platform: 'win32' },
861
+ 3: { id: 3, localname: 'MeshService.exe', rname: 'meshagent.exe', desc: 'Windows x86-32 service', update: true, amt: true, platform: 'win32' },
862
+ 4: { id: 4, localname: 'MeshService64.exe', rname: 'meshagent.exe', desc: 'Windows x86-64 service', update: true, amt: true, platform: 'win32' },
863
+ 5: { id: 5, localname: 'meshagent_x86', rname: 'meshagent', desc: 'Linux x86-32', update: true, amt: true, platform: 'linux' },
864
+ 6: { id: 6, localname: 'meshagent_x86-64', rname: 'meshagent', desc: 'Linux x86-64', update: true, amt: true, platform: 'linux' },
865
+ 7: { id: 7, localname: 'meshagent_mips', rname: 'meshagent', desc: 'Linux MIPS', update: true, amt: false, platform: 'linux' },
866
+ 8: { id: 8, localname: 'MeshAgent-Linux-XEN-x86-32', rname: 'meshagent', desc: 'XEN x86-64', update: true, amt: false, platform: 'linux' },
867
+ 9: { id: 9, localname: 'meshagent_arm', rname: 'meshagent', desc: 'Linux ARM5', update: true, amt: false, platform: 'linux' },
868
+ 10: { id: 10, localname: 'MeshAgent-Linux-ARM-PlugPC', rname: 'meshagent', desc: 'Linux ARM PlugPC', update: true, amt: false, platform: 'linux' },
869
+ 11: { id: 11, localname: 'MeshAgent-OSX-x86-32', rname: 'meshosx', desc: 'Apple OSX x86-32', update: true, amt: false, platform: 'linux' },
870
+ 12: { id: 12, localname: 'MeshAgent-Android-x86', rname: 'meshandroid', desc: 'Android x86-32', update: true, amt: false, platform: 'linux' },
871
+ 13: { id: 13, localname: 'meshagent_pogo', rname: 'meshagent', desc: 'Linux ARM PogoPlug', update: true, amt: false, platform: 'linux' },
872
+ 14: { id: 14, localname: 'MeshAgent-Android-APK', rname: 'meshandroid', desc: 'Android Market', update: false, amt: false, platform: 'android' }, // Get this one from Google Play
873
+ 15: { id: 15, localname: 'meshagent_poky', rname: 'meshagent', desc: 'Linux Poky x86-32', update: true, amt: false, platform: 'linux' },
874
+ 16: { id: 16, localname: 'MeshAgent-OSX-x86-64', rname: 'meshagent', desc: 'Apple OSX x86-64', update: true, amt: false, platform: 'osx' },
875
+ 17: { id: 17, localname: 'MeshAgent-ChromeOS', rname: 'meshagent', desc: 'Google ChromeOS', update: false, amt: false, platform: 'chromeos' }, // Get this one from Chrome store
876
+ 18: { id: 18, localname: 'meshagent_poky64', rname: 'meshagent', desc: 'Linux Poky x86-64', update: true, amt: false, platform: 'linux' },
877
+ 19: { id: 19, localname: 'meshagent_x86_nokvm', rname: 'meshagent', desc: 'Linux x86-32 NoKVM', update: true, amt: true, platform: 'linux' },
878
+ 20: { id: 20, localname: 'meshagent_x86-64_nokvm', rname: 'meshagent', desc: 'Linux x86-64 NoKVM', update: true, amt: true, platform: 'linux' },
879
+ 21: { id: 21, localname: 'MeshAgent-WinMinCore-Console-x86-32.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Console x86-32', update: true, amt: false, platform: 'win32' },
880
+ 22: { id: 22, localname: 'MeshAgent-WinMinCore-Service-x86-64.exe', rname: 'meshagent.exe', desc: 'Windows MinCore Service x86-32', update: true, amt: false, platform: 'win32' },
881
+ 23: { id: 23, localname: 'MeshAgent-NodeJS', rname: 'meshagent', desc: 'NodeJS', update: false, amt: false, platform: 'node' }, // Get this one from NPM
882
+ 24: { id: 24, localname: 'meshagent_arm-linaro', rname: 'meshagent', desc: 'Linux ARM Linaro', update: true, amt: false, platform: 'linux' },
883
+ 25: { id: 25, localname: 'meshagent_pi', rname: 'meshagent', desc: 'Linux ARM - Raspberry Pi', update: true, amt: false, platform: 'linux' } // "armv6l" and "armv7l"
884
};
885
886
// Update the list of available mesh agents
887
obj.updateMeshAgentsTable = function (func) {
888
var archcount = 0;
888
- for (var archid in obj.meshAgentsArchitectureNumbers) { archcount++; }
889
for (var archid in obj.meshAgentsArchitectureNumbers) {
890
var agentpath = obj.path.join(__dirname, 'agents', obj.meshAgentsArchitectureNumbers[archid].localname);
891
- var stream = null;
892
- try {
893
- stream = obj.fs.createReadStream(agentpath);
894
- stream.on('data', function (data) { this.hash.update(data, 'binary') });
895
- stream.on('error', function (data) {
896
- // If there is an error reading this file, make sure this agent is not in the agent table
897
- if (obj.meshAgentBinaries[this.info.id] != null) { delete obj.meshAgentBinaries[this.info.id]; }
898
- if ((--archcount == 0) && (func != null)) { func(); }
899
- });
900
- stream.on('end', function () {
901
- // Add the agent to the agent table with all information and the hash
902
- obj.meshAgentBinaries[this.info.id] = obj.common.Clone(this.info);
903
- obj.meshAgentBinaries[this.info.id].hash = this.hash.digest('hex');
904
- obj.meshAgentBinaries[this.info.id].path = this.agentpath;
905
- obj.meshAgentBinaries[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?id=' + this.info.id;
906
- var stats = null;
907
- try { stats = obj.fs.statSync(this.agentpath) } catch (e) { }
908
- if (stats != null) { obj.meshAgentBinaries[this.info.id].size = stats.size; }
891
+
892
+ // Fetch all the agent binary information
893
+ obj.meshAgentBinaries[archid] = obj.common.Clone(obj.meshAgentsArchitectureNumbers[archid]);
894
+ obj.meshAgentBinaries[archid].path = agentpath;
895
+ obj.meshAgentBinaries[archid].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?id=' + archid;
896
+ var stats = null;
897
+ try { stats = obj.fs.statSync(agentpath) } catch (e) { }
898
+ if ((stats != null)) {
899
+ // If file exists
900
+ archcount++;
901
+ obj.meshAgentBinaries[archid].size = stats.size;
902
+ // If this is a windows binary, pull binary information
903
+ if (obj.meshAgentsArchitectureNumbers[archid].platform == 'win32') {
904
+ try { obj.meshAgentBinaries[archid].pe = obj.exeHandler.parseWindowsExecutable(agentpath); } catch (e) { }
905
+ }
906
+ // Hash the binary
907
+ var hashStream = obj.crypto.createHash('sha384');
908
+ hashStream.archid = archid;
909
+ hashStream.on('data', function (data) {
910
+ obj.meshAgentBinaries[this.archid].hash = data.toString('hex');
911
if ((--archcount == 0) && (func != null)) { func(); }
912
});
911
- stream.info = obj.meshAgentsArchitectureNumbers[archid];
912
- stream.agentpath = agentpath;
913
- stream.hash = obj.crypto.createHash('sha384', stream);
914
- } catch (e) { if ((--archcount == 0) && (func != null)) { func(); } }
913
+ var options = { sourcePath: agentpath, targetStream: hashStream, platform: obj.meshAgentsArchitectureNumbers[archid].platform };
914
+ if (obj.meshAgentBinaries[archid].pe != null) { options.peinfo = obj.meshAgentBinaries[archid].pe; }
915
+ obj.exeHandler.hashExecutableFile(options);
916
+ }
917
}
918
}
919
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.1.2-u",
3
+ "version": "0.1.2-s",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
views/default.handlebars
+4
-3
@@ -1577,9 +1577,10 @@
1577
x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=2>Windows (UnInstall)</option><option value=3>Linux (UnInstall)</option></select>') + '<hr>';
1578
1579
// Windows agent install
1580
- x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
1581
- x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3" target="_blank">Windows executable (.exe)</a>');
1582
- x += addHtmlValue('Settings File', '<a href="meshsettings?id=' + meshid.split('/')[2] + '" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>');
1580
+ //x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
1581
+ x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and install it the computer to manage. This agent has server and mesh information embedded within it.<br /><br />";
1582
+ x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '" target="_blank">Windows executable (.exe)</a>');
1583
+ //x += addHtmlValue('Settings File', '<a href="meshsettings?id=' + meshid.split('/')[2] + '" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>');
1584
x += "</div>";
1585
1586
// Linux agent install
webserver.js
+37
-2
@@ -1397,12 +1397,40 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1397
1398
// Handle a request to download a mesh agent
1399
obj.handleMeshAgentRequest = function (req, res) {
1400
+ var domain = checkUserIpAddress(req, res);
1401
+ if (domain == null) return;
1402
if (req.query.id != null) {
1403
// Send a specific mesh agent back
1404
var argentInfo = obj.parent.meshAgentBinaries[req.query.id];
1405
if (argentInfo == null) { res.sendStatus(404); return; }
1404
- res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + argentInfo.rname });
1405
- res.sendFile(argentInfo.path);
1406
+ if ((req.query.meshid == null) || (argentInfo.platform != 'win32')) {
1407
+ res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + argentInfo.rname });
1408
+ res.sendFile(argentInfo.path);
1409
+ } else {
1410
+ // We are going to embed the .msh file into the Windows executable (signed or not).
1411
+ // First, query the meshid to build the .msh file
1412
+ obj.db.Get('mesh/' + domain.id + '/' + req.query.meshid, function (err, meshes) {
1413
+ if (meshes.length != 1) { res.sendStatus(401); return; }
1414
+ var mesh = meshes[0];
1415
+
1416
+ // Check if this user has rights to do this
1417
+ //var user = obj.users[req.session.userid];
1418
+ //if ((user == null) || (mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) { res.sendStatus(401); return; }
1419
+ //if (domain.id != mesh.domain) { res.sendStatus(401); return; }
1420
+
1421
+ var meshidhex = new Buffer(req.query.meshid.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
1422
+ var serveridhex = new Buffer(obj.agentCertificateHashBase64.replace(/\@/g, '+').replace(/\$/g, '/'), 'base64').toString('hex').toUpperCase();
1423
+
1424
+ // Build the agent connection URL. If we are using a sub-domain or one with a DNS, we need to craft the URL correctly.
1425
+ var xdomain = (domain.dns == null) ? domain.id : '';
1426
+ if (xdomain != '') xdomain += "/";
1427
+ var meshsettings = "MeshName=" + mesh.name + "\r\nMeshType=" + mesh.mtype + "\r\nMeshID=0x" + meshidhex + "\r\nServerID=" + serveridhex + "\r\n";
1428
+ if (obj.args.lanonly != true) { meshsettings += "MeshServer=ws" + (obj.args.notls ? '' : 's') + "://" + getWebServerName(domain) + ":" + obj.args.port + "/" + xdomain + "agent.ashx\r\n"; } else { meshsettings += "MeshServer=local"; }
1429
+
1430
+ res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + argentInfo.rname });
1431
+ obj.parent.exeHandler.streamExeWithMeshPolicy({ platform: 'win32', sourceFileName: obj.parent.meshAgentBinaries[req.query.id].path, destinationStream: res, msh: meshsettings, peinfo: obj.parent.meshAgentBinaries[req.query.id].pe });
1432
+ });
1433
+ }
1434
} else if (req.query.script != null) {
1435
// Send a specific mesh install script back
1436
var scriptInfo = obj.parent.meshAgentInstallScripts[req.query.script];
@@ -1413,6 +1441,11 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1441
// Send meshcmd for a specific platform back
1442
var argentInfo = obj.parent.meshAgentBinaries[req.query.meshcmd];
1443
if ((argentInfo == null) || (obj.parent.defaultMeshCmd == null)) { res.sendStatus(404); return; }
1444
+ res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') });
1445
+ res.statusCode = 200;
1446
+ obj.parent.exeHandler.streamExeWithJavaScript({ platform: argentInfo.platform, sourceFileName: argentInfo.path, destinationStream: res, js: new Buffer(obj.parent.defaultMeshCmd, 'utf8'), peinfo: argentInfo.pe });
1447
+
1448
+ /*
1449
// Load the agent
1450
obj.fs.readFile(argentInfo.path, function (err, agentexe) {
1451
if (err != null) { res.sendStatus(404); return; }
@@ -1423,6 +1456,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1456
tail.writeInt32BE(agentexe.length + meshcmdbuf.length + 8, 4);
1457
res.send(Buffer.concat([agentexe, meshcmdbuf, tail]));
1458
});
1459
+ */
1460
} else if (req.query.meshaction != null) {
1461
var domain = checkUserIpAddress(req, res);
1462
if (domain == null) { res.sendStatus(404); return; }
@@ -1773,3 +1807,4 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1807
1808
return obj;
1809
}
1810
+