Fixed support for Self-Update on Windows when running in Temporary Mode

Bryan Roe committed Jun 3, 2021 at 09:57 UTC bcb45580bd726141e5e1c53ef382a330e9a34a22
1 file changed +180 -31
agents/recoverycore.js
+180 -31
@@ -6,9 +6,83 @@ var nextTunnelIndex = 1;
6 var tunnels = {};
7 var fs = require('fs');
8
9 -if (require('MeshAgent').ARCHID == null) {
9 +
10 +function _getPotentialServiceNames()
11 +{
12 + var registry = require('win-registry');
13 + var ret = [];
14 + var K = registry.QueryKey(registry.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services');
15 + var service, s;
16 + while (K.subkeys.length > 0)
17 + {
18 + service = K.subkeys.shift();
19 + try
20 + {
21 + s = registry.QueryKey(registry.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + service, 'ImagePath');
22 + if (s.startsWith(process.execPath) || s.startsWith('"' + process.execPath + '"'))
23 + {
24 + ret.push(service);
25 + }
26 + }
27 + catch (x)
28 + {
29 + }
30 + }
31 + return (ret);
32 +}
33 +function _verifyServiceName(names)
34 +{
35 + var i;
36 + var s;
37 + var ret = null;
38 + for (i = 0; i < names.length; ++i)
39 + {
40 + try
41 + {
42 + s = require('service-manager').manager.getService(names[i]);
43 + if (s.isMe())
44 + {
45 + ret = names[i];
46 + s.close();
47 + break;
48 + }
49 + s.close();
50 + }
51 + catch (z) { }
52 + }
53 + return (ret);
54 +}
55 +
56 +function windows_getCommandLine()
57 +{
58 + var parms = [];
59 + var GM = require('_GenericMarshal');
60 + var k32 = GM.CreateNativeProxy('kernel32.dll');
61 + var s32 = GM.CreateNativeProxy('shell32.dll');
62 + k32.CreateMethod('GetCommandLineW');
63 + k32.CreateMethod('LocalFree');
64 + s32.CreateMethod('CommandLineToArgvW');
65 + var v = k32.GetCommandLineW();
66 + var i;
67 + var len = GM.CreateVariable(4);
68 + var val = s32.CommandLineToArgvW(v, len);
69 + len = len.toBuffer().readInt32LE(0);
70 + if (len > 0)
71 + {
72 + for (i = 0; i < len; ++i)
73 + {
74 + parms.push(val.Deref(i * GM.PointerSize, GM.PointerSize).Deref().Wide2UTF8);
75 + }
76 + }
77 + k32.LocalFree(val);
78 + return (parms);
79 +}
80 +
81 +if (require('MeshAgent').ARCHID == null)
82 +{
83 var id = null;
11 - switch (process.platform) {
84 + switch (process.platform)
85 + {
86 case 'win32':
87 id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
88 break;
@@ -16,10 +90,12 @@ if (require('MeshAgent').ARCHID == null) {
90 id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
91 break;
92 case 'darwin':
19 - try {
93 + try
94 + {
95 id = require('os').arch() == 'x64' ? 16 : 29;
96 }
22 - catch (xx) {
97 + catch (xx)
98 + {
99 id = 16;
100 }
101 break;
@@ -29,17 +105,22 @@ if (require('MeshAgent').ARCHID == null) {
105
106 //attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); });
107
32 -function sendConsoleText(msg, sessionid) {
33 - if (sessionid != null) {
108 +function sendConsoleText(msg, sessionid)
109 +{
110 + if (sessionid != null)
111 + {
112 require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: msg, sessionid: sessionid });
113 }
36 - else {
114 + else
115 + {
116 require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: msg });
117 }
118 }
119
41 -function sendAgentMessage(msg, icon) {
42 - if (sendAgentMessage.messages == null) {
120 +function sendAgentMessage(msg, icon)
121 +{
122 + if (sendAgentMessage.messages == null)
123 + {
124 sendAgentMessage.messages = {};
125 sendAgentMessage.nextid = 1;
126 }
@@ -48,9 +129,11 @@ function sendAgentMessage(msg, icon) {
129 }
130
131 // Add to the server event log
51 -function MeshServerLog(msg, state) {
132 +function MeshServerLog(msg, state)
133 +{
134 if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
53 - if (state) {
135 + if (state)
136 + {
137 if (state.userid) { msg.userid = state.userid; }
138 if (state.username) { msg.username = state.username; }
139 if (state.sessionid) { msg.sessionid = state.sessionid; }
@@ -60,9 +143,11 @@ function MeshServerLog(msg, state) {
143 }
144
145 // Add to the server event log, use internationalized events
63 -function MeshServerLogEx(id, args, msg, state) {
146 +function MeshServerLogEx(id, args, msg, state)
147 +{
148 var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
65 - if (state) {
149 + if (state)
150 + {
151 if (state.userid) { msg.userid = state.userid; }
152 if (state.username) { msg.username = state.username; }
153 if (state.sessionid) { msg.sessionid = state.sessionid; }
@@ -346,6 +431,17 @@ function windows_execve(name, agentfilename, sessionid) {
431 var arg2 = require('_GenericMarshal').CreateVariable('/C wmic service "' + name + '" call stopservice & "' + cwd + agentfilename + '.update.exe" -b64exec ' + 'dHJ5CnsKICAgIHZhciBzZXJ2aWNlTG9jYXRpb24gPSBwcm9jZXNzLmFyZ3YucG9wKCk7CiAgICByZXF1aXJlKCdwcm9jZXNzLW1hbmFnZXInKS5lbnVtZXJhdGVQcm9jZXNzZXMoKS50aGVuKGZ1bmN0aW9uIChwcm9jKQogICAgewogICAgICAgIGZvciAodmFyIHAgaW4gcHJvYykKICAgICAgICB7CiAgICAgICAgICAgIGlmIChwcm9jW3BdLnBhdGggPT0gc2VydmljZUxvY2F0aW9uKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBwcm9jZXNzLmtpbGwocHJvY1twXS5waWQpOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHByb2Nlc3MuZXhpdCgpOwogICAgfSk7Cn0KY2F0Y2goZSkKewogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' +
432 ' "' + process.execPath + '" & copy "' + cwd + agentfilename + '.update.exe" "' + process.execPath + '" & wmic service "' + name + '" call startservice & erase "' + cwd + agentfilename + '.update.exe"', { wide: true });
433
434 + if (name == null)
435 + {
436 + // We can continue with self update for Temp/Console Mode on Windows
437 + var db = process.execPath.split('.exe'); db.pop(); db = db.join('.exe') + '.db';
438 + var update = cwd + agentfilename + '.update.exe';
439 + var updatedb = cwd + agentfilename + '.update.db';
440 + var parms = windows_getCommandLine(); parms.shift();
441 + var tmp = '/C copy "' + db + '" "' + updatedb + '" & "' + update + '" ' + parms.join(' ') + ' & move "' + update + '" "' + process.execPath + '" & move "' + updatedb + '" "' + db + '"';
442 + arg2 = require('_GenericMarshal').CreateVariable(tmp, { wide: true });
443 + }
444 +
445 arg1.pointerBuffer().copy(args.toBuffer());
446 arg2.pointerBuffer().copy(args.toBuffer(), require('_GenericMarshal').PointerSize);
447
@@ -363,7 +459,8 @@ function agentUpdate_Start(updateurl, updateoptions) {
459 if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
460 }
461
366 - if (agentUpdate_Start._selfupdate != null) {
462 + if (agentUpdate_Start._selfupdate != null)
463 + {
464 // We were already called, so we will ignore this duplicate request
465 if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
466 }
@@ -373,25 +470,58 @@ function agentUpdate_Start(updateurl, updateoptions) {
470 // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
471 sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
472 }
376 - else {
473 + else
474 + {
475 var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
476 var name = require('MeshAgent').serviceName;
379 - if (name == null) { name = (process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); } // This is an older agent that doesn't expose the service name, so use the default
380 - try {
381 - var s = require('service-manager').manager.getService(name);
382 - if (!s.isMe()) {
477 + if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; }
478 + if (process.platform == 'win32')
479 + {
480 + // Special Processing for Temporary/Console Mode Agents on Windows
481 + var parms = windows_getCommandLine();
482 + if (parms.findIndex(function (val) { return (val.toUpperCase() == 'RUN' || val.toUpperCase() == 'CONNECT');})>=0)
483 + {
484 + // This is a Temporary/Console Mode Agent
485 + sendConsoleText('This is a temporary/console agent, checking for conflicts with background services...');
486 +
487 + // Check to see if our binary conflicts with an installed agent
488 + var agents = _getPotentialServiceNames();
489 + if(_getPotentialServiceNames().length>0)
490 + {
491 + sendConsoleText('Self update cannot continue because the installed agent (' + agents[0] + ') conflicts with the currently running Temp/Console agent...', sessionid);
492 + return;
493 + }
494 +
495 +
496 + sendConsoleText('No conflicts detected...');
497 + name = null;
498 + }
499 + else
500 + {
501 + // Not running in Temp/Console Mode... No Op here....
502 + }
503 + }
504 + else
505 + {
506 + // Non Windows Self Update
507 + try
508 + {
509 + var s = require('service-manager').manager.getService(name);
510 + if (!s.isMe())
511 + {
512 + if (process.platform == 'win32') { s.close(); }
513 + sendConsoleText('Self Update cannot continue, this agent is not an instance of background service (' + name + ')', sessionid);
514 + return;
515 + }
516 if (process.platform == 'win32') { s.close(); }
384 - sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
517 + }
518 + catch (zz)
519 + {
520 + sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
521 + sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
522 return;
523 }
387 - if (process.platform == 'win32') { s.close(); }
388 - }
389 - catch (zz) {
390 - sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
391 - sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
392 - return;
524 }
394 -
525 if ((sessionid != null) && (updateurl != null)) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
526 var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
527 options.protocol = 'https:';
@@ -1007,7 +1137,25 @@ require('MeshAgent').AddCommandHandler(function (data) {
1137 function processConsoleCommand(cmd, args, rights, sessionid) {
1138 try {
1139 var response = null;
1010 - switch (cmd) {
1140 + switch (cmd)
1141 + {
1142 + default:
1143 + { // This is an unknown command, return an error message
1144 + response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
1145 + break;
1146 + }
1147 + case 'commandline':
1148 + {
1149 + if (process.platform == 'win32')
1150 + {
1151 + response = JSON.stringify(windows_getCommandLine(), null, 1);
1152 + }
1153 + else
1154 + {
1155 + response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
1156 + }
1157 + }
1158 + break;
1159 case 'help':
1160 response = "Available commands are: agentupdate, agentupdateex, dbkeys, dbget, dbset, dbcompact, eval, netinfo, osinfo, setdebug, versions.";
1161 break;
@@ -1097,10 +1245,11 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
1245 response = objToString(interfaces, 0, ' ', true);
1246 break;
1247 }
1100 - default: { // This is an unknown command, return an error message
1101 - response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.';
1248 + case 'name':
1249 + {
1250 + response = 'Service Name = ' + require('MeshAgent').serviceName;
1251 + }
1252 break;
1103 - }
1253 }
1254 } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
1255 if (response != null) { sendConsoleText(response, sessionid); }