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 +