mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-03-09 15:40:18 +00:00
Added remote process control
This commit is contained in:
parent
d05f086a0e
commit
fb55e44edf
14 changed files with 448 additions and 47 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -30,6 +30,7 @@ function createMeshCore(agent) {
|
|||
var net = require('net');
|
||||
var fs = require('fs');
|
||||
var rtc = require('ILibWebRTC');
|
||||
var processManager = require('process-manager');
|
||||
var SMBiosTables = require('smbios');
|
||||
var amtMei = null, amtLms = null, amtLmsState = 0;
|
||||
var amtMeiConnected = 0, amtMeiTmpState = null;
|
||||
|
@ -327,39 +328,55 @@ function createMeshCore(agent) {
|
|||
// If this is a console command, parse it and call the console handler
|
||||
switch (data.action) {
|
||||
case 'msg': {
|
||||
if (data.type == 'console') { // Process a console command
|
||||
if (data.value && data.sessionid) {
|
||||
var args = splitArgs(data.value);
|
||||
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
|
||||
switch (data.type) {
|
||||
case 'console': { // Process a console command
|
||||
if (data.value && data.sessionid) {
|
||||
var args = splitArgs(data.value);
|
||||
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'tunnel': {
|
||||
if (data.value != null) { // Process a new tunnel connection request
|
||||
// Create a new tunnel object
|
||||
var xurl = getServerTargetUrlEx(data.value);
|
||||
if (xurl != null) {
|
||||
var woptions = http.parseUri(xurl);
|
||||
woptions.rejectUnauthorized = 0;
|
||||
//sendConsoleText(JSON.stringify(woptions));
|
||||
var tunnel = http.request(woptions);
|
||||
tunnel.upgrade = onTunnelUpgrade;
|
||||
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
|
||||
tunnel.sessionid = data.sessionid;
|
||||
tunnel.rights = data.rights;
|
||||
tunnel.state = 0;
|
||||
tunnel.url = xurl;
|
||||
tunnel.protocol = 0;
|
||||
tunnel.tcpaddr = data.tcpaddr;
|
||||
tunnel.tcpport = data.tcpport;
|
||||
tunnel.end();
|
||||
// Put the tunnel in the tunnels list
|
||||
var index = nextTunnelIndex++;;
|
||||
tunnel.index = index;
|
||||
tunnels[index] = tunnel;
|
||||
|
||||
sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ps': {
|
||||
if (data.sessionid) {
|
||||
processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'pskill': {
|
||||
sendConsoleText(JSON.stringify(data));
|
||||
try { process.kill(data.value); } catch (e) { sendConsoleText(JSON.stringify(e)); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((data.type == 'tunnel') && (data.value != null)) { // Process a new tunnel connection request
|
||||
// Create a new tunnel object
|
||||
var xurl = getServerTargetUrlEx(data.value);
|
||||
if (xurl != null) {
|
||||
var woptions = http.parseUri(xurl);
|
||||
woptions.rejectUnauthorized = 0;
|
||||
//sendConsoleText(JSON.stringify(woptions));
|
||||
var tunnel = http.request(woptions);
|
||||
tunnel.upgrade = onTunnelUpgrade;
|
||||
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
|
||||
tunnel.sessionid = data.sessionid;
|
||||
tunnel.rights = data.rights;
|
||||
tunnel.state = 0;
|
||||
tunnel.url = xurl;
|
||||
tunnel.protocol = 0;
|
||||
tunnel.tcpaddr = data.tcpaddr;
|
||||
tunnel.tcpport = data.tcpport;
|
||||
tunnel.end();
|
||||
// Put the tunnel in the tunnels list
|
||||
var index = nextTunnelIndex++;;
|
||||
tunnel.index = index;
|
||||
tunnels[index] = tunnel;
|
||||
|
||||
sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'wakeonlan': {
|
||||
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
|
||||
|
@ -819,7 +836,7 @@ function createMeshCore(agent) {
|
|||
var response = null;
|
||||
switch (cmd) {
|
||||
case 'help': { // Displays available commands
|
||||
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios.';
|
||||
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios.';
|
||||
break;
|
||||
}
|
||||
case 'setdebug': {
|
||||
|
@ -827,6 +844,23 @@ function createMeshCore(agent) {
|
|||
else { if (args['_'][0] == '*') { console.setDestination(1); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
|
||||
break;
|
||||
}
|
||||
case 'ps': {
|
||||
processManager.getProcesses(function (plist) {
|
||||
var x = '';
|
||||
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
|
||||
sendConsoleText(x, sessionid);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'kill': {
|
||||
if ((args['_'].length < 1)) {
|
||||
response = 'Proper usage: kill [pid]'; // Display correct command usage
|
||||
} else {
|
||||
process.kill(parseInt(args['_'][0]));
|
||||
response = 'Killed process ' + args['_'][0] + '.';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'smbios': {
|
||||
if (SMBiosTables != null) {
|
||||
SMBiosTables.get(function (data) {
|
||||
|
|
102
agents/modules_meshcmd/UserSessions.js
Normal file
102
agents/modules_meshcmd/UserSessions.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
|
||||
|
||||
function UserSessions()
|
||||
{
|
||||
this._ObjectID = 'UserSessions';
|
||||
|
||||
if (process.platform == 'win32') {
|
||||
this._marshal = require('_GenericMarshal');
|
||||
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
|
||||
this._kernel32.CreateMethod('GetLastError');
|
||||
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
|
||||
this._wts.CreateMethod('WTSEnumerateSessionsA');
|
||||
this._wts.CreateMethod('WTSQuerySessionInformationA');
|
||||
this._wts.CreateMethod('WTSFreeMemory');
|
||||
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
|
||||
this.InfoClass =
|
||||
{
|
||||
'WTSInitialProgram': 0,
|
||||
'WTSApplicationName': 1,
|
||||
'WTSWorkingDirectory': 2,
|
||||
'WTSOEMId': 3,
|
||||
'WTSSessionId': 4,
|
||||
'WTSUserName': 5,
|
||||
'WTSWinStationName': 6,
|
||||
'WTSDomainName': 7,
|
||||
'WTSConnectState': 8,
|
||||
'WTSClientBuildNumber': 9,
|
||||
'WTSClientName': 10,
|
||||
'WTSClientDirectory': 11,
|
||||
'WTSClientProductId': 12,
|
||||
'WTSClientHardwareId': 13,
|
||||
'WTSClientAddress': 14,
|
||||
'WTSClientDisplay': 15,
|
||||
'WTSClientProtocolType': 16,
|
||||
'WTSIdleTime': 17,
|
||||
'WTSLogonTime': 18,
|
||||
'WTSIncomingBytes': 19,
|
||||
'WTSOutgoingBytes': 20,
|
||||
'WTSIncomingFrames': 21,
|
||||
'WTSOutgoingFrames': 22,
|
||||
'WTSClientInfo': 23,
|
||||
'WTSSessionInfo': 24,
|
||||
'WTSSessionInfoEx': 25,
|
||||
'WTSConfigInfo': 26,
|
||||
'WTSValidationInfo': 27,
|
||||
'WTSSessionAddressV4': 28,
|
||||
'WTSIsRemoteSession': 29
|
||||
};
|
||||
|
||||
this.getSessionAttribute = function getSessionAttribute(sessionId, attr)
|
||||
{
|
||||
var buffer = this._marshal.CreatePointer();
|
||||
var bytesReturned = this._marshal.CreateVariable(4);
|
||||
|
||||
if (this._wts.WTSQuerySessionInformationA(0, sessionId, attr, buffer, bytesReturned).Val == 0)
|
||||
{
|
||||
throw ('Error calling WTSQuerySessionInformation: ' + this._kernel32.GetLastError.Val);
|
||||
}
|
||||
|
||||
var retVal = buffer.Deref().String;
|
||||
|
||||
this._wts.WTSFreeMemory(buffer.Deref());
|
||||
return (retVal);
|
||||
};
|
||||
|
||||
this.Current = function Current()
|
||||
{
|
||||
var retVal = {};
|
||||
var pinfo = this._marshal.CreatePointer();
|
||||
var count = this._marshal.CreateVariable(4);
|
||||
if (this._wts.WTSEnumerateSessionsA(0, 0, 1, pinfo, count).Val == 0)
|
||||
{
|
||||
throw ('Error calling WTSEnumerateSessionsA: ' + this._kernel32.GetLastError().Val);
|
||||
}
|
||||
|
||||
for (var i = 0; i < count.toBuffer().readUInt32LE() ; ++i)
|
||||
{
|
||||
var info = pinfo.Deref().Deref(i * (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize == 4 ? 12 : 24);
|
||||
var j = { SessionId: info.toBuffer().readUInt32LE() };
|
||||
j.StationName = info.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize).Deref().String;
|
||||
j.State = this.SessionStates[info.Deref(this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE()];
|
||||
if (j.State == 'Active') {
|
||||
j.Username = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSUserName);
|
||||
j.Domain = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSDomainName);
|
||||
}
|
||||
retVal[j.SessionId] = j;
|
||||
}
|
||||
|
||||
this._wts.WTSFreeMemory(pinfo.Deref());
|
||||
return (retVal);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Current = function Current()
|
||||
{
|
||||
return ({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new UserSessions();
|
102
agents/modules_meshcore/UserSessions.js
Normal file
102
agents/modules_meshcore/UserSessions.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
|
||||
|
||||
function UserSessions()
|
||||
{
|
||||
this._ObjectID = 'UserSessions';
|
||||
|
||||
if (process.platform == 'win32') {
|
||||
this._marshal = require('_GenericMarshal');
|
||||
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
|
||||
this._kernel32.CreateMethod('GetLastError');
|
||||
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
|
||||
this._wts.CreateMethod('WTSEnumerateSessionsA');
|
||||
this._wts.CreateMethod('WTSQuerySessionInformationA');
|
||||
this._wts.CreateMethod('WTSFreeMemory');
|
||||
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
|
||||
this.InfoClass =
|
||||
{
|
||||
'WTSInitialProgram': 0,
|
||||
'WTSApplicationName': 1,
|
||||
'WTSWorkingDirectory': 2,
|
||||
'WTSOEMId': 3,
|
||||
'WTSSessionId': 4,
|
||||
'WTSUserName': 5,
|
||||
'WTSWinStationName': 6,
|
||||
'WTSDomainName': 7,
|
||||
'WTSConnectState': 8,
|
||||
'WTSClientBuildNumber': 9,
|
||||
'WTSClientName': 10,
|
||||
'WTSClientDirectory': 11,
|
||||
'WTSClientProductId': 12,
|
||||
'WTSClientHardwareId': 13,
|
||||
'WTSClientAddress': 14,
|
||||
'WTSClientDisplay': 15,
|
||||
'WTSClientProtocolType': 16,
|
||||
'WTSIdleTime': 17,
|
||||
'WTSLogonTime': 18,
|
||||
'WTSIncomingBytes': 19,
|
||||
'WTSOutgoingBytes': 20,
|
||||
'WTSIncomingFrames': 21,
|
||||
'WTSOutgoingFrames': 22,
|
||||
'WTSClientInfo': 23,
|
||||
'WTSSessionInfo': 24,
|
||||
'WTSSessionInfoEx': 25,
|
||||
'WTSConfigInfo': 26,
|
||||
'WTSValidationInfo': 27,
|
||||
'WTSSessionAddressV4': 28,
|
||||
'WTSIsRemoteSession': 29
|
||||
};
|
||||
|
||||
this.getSessionAttribute = function getSessionAttribute(sessionId, attr)
|
||||
{
|
||||
var buffer = this._marshal.CreatePointer();
|
||||
var bytesReturned = this._marshal.CreateVariable(4);
|
||||
|
||||
if (this._wts.WTSQuerySessionInformationA(0, sessionId, attr, buffer, bytesReturned).Val == 0)
|
||||
{
|
||||
throw ('Error calling WTSQuerySessionInformation: ' + this._kernel32.GetLastError.Val);
|
||||
}
|
||||
|
||||
var retVal = buffer.Deref().String;
|
||||
|
||||
this._wts.WTSFreeMemory(buffer.Deref());
|
||||
return (retVal);
|
||||
};
|
||||
|
||||
this.Current = function Current()
|
||||
{
|
||||
var retVal = {};
|
||||
var pinfo = this._marshal.CreatePointer();
|
||||
var count = this._marshal.CreateVariable(4);
|
||||
if (this._wts.WTSEnumerateSessionsA(0, 0, 1, pinfo, count).Val == 0)
|
||||
{
|
||||
throw ('Error calling WTSEnumerateSessionsA: ' + this._kernel32.GetLastError().Val);
|
||||
}
|
||||
|
||||
for (var i = 0; i < count.toBuffer().readUInt32LE() ; ++i)
|
||||
{
|
||||
var info = pinfo.Deref().Deref(i * (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize == 4 ? 12 : 24);
|
||||
var j = { SessionId: info.toBuffer().readUInt32LE() };
|
||||
j.StationName = info.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize).Deref().String;
|
||||
j.State = this.SessionStates[info.Deref(this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE()];
|
||||
if (j.State == 'Active') {
|
||||
j.Username = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSUserName);
|
||||
j.Domain = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSDomainName);
|
||||
}
|
||||
retVal[j.SessionId] = j;
|
||||
}
|
||||
|
||||
this._wts.WTSFreeMemory(pinfo.Deref());
|
||||
return (retVal);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Current = function Current()
|
||||
{
|
||||
return ({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new UserSessions();
|
96
agents/modules_meshcore/process-manager.js
Normal file
96
agents/modules_meshcore/process-manager.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2018 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
var GM = require('_GenericMarshal');
|
||||
|
||||
function processManager() {
|
||||
this._ObjectID = 'processManager';
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
this._kernel32 = GM.CreateNativeProxy('kernel32.dll');
|
||||
this._kernel32.CreateMethod('GetLastError');
|
||||
this._kernel32.CreateMethod('CreateToolhelp32Snapshot');
|
||||
this._kernel32.CreateMethod('Process32First');
|
||||
this._kernel32.CreateMethod('Process32Next');
|
||||
break;
|
||||
case 'linux':
|
||||
this._childProcess = require('child_process');
|
||||
break;
|
||||
default:
|
||||
throw (process.platform + ' not supported');
|
||||
}
|
||||
this.getProcesses = function getProcesses(callback) {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
var h = this._kernel32.CreateToolhelp32Snapshot(2, 0), info = GM.CreateVariable(304), retVal = {};
|
||||
info.toBuffer().writeUInt32LE(304, 0);
|
||||
var nextProcess = this._kernel32.Process32First(h, info);
|
||||
while (nextProcess.Val) {
|
||||
retVal[info.Deref(8, 4).toBuffer().readUInt32LE(0)] = { cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String };
|
||||
nextProcess = this._kernel32.Process32Next(h, info);
|
||||
}
|
||||
if (callback) { callback.apply(this, [retVal]); }
|
||||
break;
|
||||
case 'linux':
|
||||
if (!this._psp) { this._psp = {}; }
|
||||
var p = this._childProcess.execFile("/bin/ps", ["ps", "-uxa"], { type: this._childProcess.SpawnTypes.TERM });
|
||||
this._psp[p.pid] = p;
|
||||
p.Parent = this;
|
||||
p.ps = '';
|
||||
p.callback = callback;
|
||||
p.args = [];
|
||||
for (var i = 1; i < arguments.length; ++i) { p.args.push(arguments[i]); }
|
||||
p.on('exit', function onGetProcesses() {
|
||||
delete this.Parent._psp[this.pid];
|
||||
var retVal = {}, lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0;
|
||||
for (var i in lines) {
|
||||
var tokens = lines[i].split(' '), tokenList = [];
|
||||
for (var x in tokens) {
|
||||
if (i == 0 && tokens[x]) { key[tokens[x]] = keyi++; }
|
||||
if (i > 0 && tokens[x]) { tokenList.push(tokens[x]); }
|
||||
}
|
||||
if ((i > 0) && (tokenList[key.PID])) {
|
||||
retVal[tokenList[key.PID]] = { user: tokenList[key.USER], cmd: tokenList[key.COMMAND] };
|
||||
}
|
||||
}
|
||||
if (this.callback) {
|
||||
this.args.unshift(retVal);
|
||||
this.callback.apply(this.parent, this.args);
|
||||
}
|
||||
});
|
||||
p.stdout.on('data', function (chunk) { this.parent.ps += chunk.toString(); });
|
||||
break;
|
||||
default:
|
||||
throw ('Enumerating processes on ' + process.platform + ' not supported');
|
||||
}
|
||||
};
|
||||
this.getProcessInfo = function getProcessInfo(pid) {
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
var status = require('fs').readFileSync('/proc/' + pid + '/status'), lines = status.toString().split('\n'), info = {};
|
||||
for (var i in lines) {
|
||||
var tokens = lines[i].split(':');
|
||||
if (tokens.length > 1) { tokens[1] = tokens[1].trim(); }
|
||||
info[tokens[0]] = tokens[1];
|
||||
}
|
||||
return info;
|
||||
default:
|
||||
throw ('getProcessInfo() not supported for ' + process.platform);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = new processManager();
|
Loading…
Add table
Add a link
Reference in a new issue