From 6797f8199c2c6ef2504a71bedd39c662b18d25bc Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sun, 8 May 2022 11:46:07 -0700 Subject: [PATCH] Added RDP alternate shell / working dir, #3943 --- apprelays.js | 6 +- public/scripts/agent-rdp-0.0.1.js | 2 +- rdp/protocol/rdp.js | 442 +++++++++++++++--------------- views/default.handlebars | 21 +- 4 files changed, 243 insertions(+), 228 deletions(-) diff --git a/apprelays.js b/apprelays.js index 18c28ed6..31168edb 100644 --- a/apprelays.js +++ b/apprelays.js @@ -179,7 +179,11 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) { screen: obj.infos.screen, locale: obj.infos.locale, }; - if (obj.infos.options && (obj.infos.options.flags != null)) { args.perfFlags = obj.infos.options.flags; delete obj.infos.options.flags; } + if (obj.infos.options) { + if (obj.infos.options.flags != null) { args.perfFlags = obj.infos.options.flags; delete obj.infos.options.flags; } + if ((obj.infos.options.workingDir != null) && (obj.infos.options.workingDir != '')) { args.workingDir = obj.infos.options.workingDir; } + if ((obj.infos.options.alternateShell != null) && (obj.infos.options.alternateShell != '')) { args.alternateShell = obj.infos.options.alternateShell; } + } rdpClient = require('./rdp').createClient(args).on('connect', function () { send(['rdp-connect']); if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed diff --git a/public/scripts/agent-rdp-0.0.1.js b/public/scripts/agent-rdp-0.0.1.js index 7be6b415..698ee7e0 100644 --- a/public/scripts/agent-rdp-0.0.1.js +++ b/public/scripts/agent-rdp-0.0.1.js @@ -27,7 +27,7 @@ var CreateRDPDesktop = function (canvasid) { obj.nodeid = nodeid; obj.port = port; obj.credentials = credentials; - var options = { savepass: credentials.savecred, useServerCreds: credentials.servercred, width: credentials.width, height: credentials.height, flags: credentials.flags }; + var options = { savepass: credentials.savecred, useServerCreds: credentials.servercred, width: credentials.width, height: credentials.height, flags: credentials.flags, workingDir: credentials.workdir, alternateShell: credentials.altshell }; if (credentials.width && credentials.height) { options.width = obj.ScreenWidth = obj.width = credentials.width; options.height = obj.ScreenHeight = obj.height = credentials.height; diff --git a/rdp/protocol/rdp.js b/rdp/protocol/rdp.js index b830371d..f9fac6f8 100644 --- a/rdp/protocol/rdp.js +++ b/rdp/protocol/rdp.js @@ -33,81 +33,81 @@ var pdu = require('./pdu'); * decompress bitmap from RLE algorithm * @param bitmap {object} bitmap object of bitmap event of node-rdpjs */ -function decompress (bitmap) { - var fName = null; - switch (bitmap.bitsPerPixel.value) { - case 15: - fName = 'bitmap_decompress_15'; - break; - case 16: - fName = 'bitmap_decompress_16'; - break; - case 24: - fName = 'bitmap_decompress_24'; - break; - case 32: - fName = 'bitmap_decompress_32'; - break; - default: - throw 'invalid bitmap data format'; - } - - var input = new Uint8Array(bitmap.bitmapDataStream.value); - var inputPtr = rle._malloc(input.length); - var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length); - inputHeap.set(input); - - var ouputSize = bitmap.width.value * bitmap.height.value * 4; - var outputPtr = rle._malloc(ouputSize); +function decompress(bitmap) { + var fName = null; + switch (bitmap.bitsPerPixel.value) { + case 15: + fName = 'bitmap_decompress_15'; + break; + case 16: + fName = 'bitmap_decompress_16'; + break; + case 24: + fName = 'bitmap_decompress_24'; + break; + case 32: + fName = 'bitmap_decompress_32'; + break; + default: + throw 'invalid bitmap data format'; + } - var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize); + var input = new Uint8Array(bitmap.bitmapDataStream.value); + var inputPtr = rle._malloc(input.length); + var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length); + inputHeap.set(input); - var res = rle.ccall(fName, - 'number', - ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'], - [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length] - ); - - var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize); - - rle._free(inputPtr); - rle._free(outputPtr); - - return output; + var ouputSize = bitmap.width.value * bitmap.height.value * 4; + var outputPtr = rle._malloc(ouputSize); + + var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize); + + var res = rle.ccall(fName, + 'number', + ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'], + [outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length] + ); + + var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize); + + rle._free(inputPtr); + rle._free(outputPtr); + + return output; } /** * Main RDP module */ function RdpClient(config) { - config = config || {}; - this.connected = false; - this.bufferLayer = new layer.BufferLayer(new net.Socket()); - this.tpkt = new TPKT(this.bufferLayer); - this.x224 = new x224.Client(this.tpkt, config); - this.mcs = new t125.mcs.Client(this.x224); - this.sec = new pdu.sec.Client(this.mcs, this.tpkt); - this.global = new pdu.global.Client(this.sec, this.sec); - - // config log level - log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO; - - // credentials - if (config.domain) { - this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2'); - } - if (config.userName) { - this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2'); - } - if (config.password) { - this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2'); - } - if(config.workingDir) { - this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2'); - } - if(config.alternateShell) { - this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2'); - } + config = config || {}; + this.connected = false; + this.bufferLayer = new layer.BufferLayer(new net.Socket()); + this.tpkt = new TPKT(this.bufferLayer); + this.x224 = new x224.Client(this.tpkt, config); + this.mcs = new t125.mcs.Client(this.x224); + this.sec = new pdu.sec.Client(this.mcs, this.tpkt); + this.global = new pdu.global.Client(this.sec, this.sec); + + // config log level + log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO; + + // credentials + if (config.domain) { + this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2'); + } + if (config.userName) { + this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2'); + } + if (config.password) { + this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2'); + } + if (config.workingDir) { + this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2'); + } + if (config.alternateShell) { + this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2'); + } if (config.perfFlags != null) { this.sec.infos.obj.extendedInfo.obj.performanceFlags.value = config.perfFlags; @@ -121,72 +121,72 @@ function RdpClient(config) { | pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG; } } - - if (config.autoLogin) { - this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON; - } - - if (config.screen && config.screen.width && config.screen.height) { - this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width; - this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height; - } - - log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value); - - // config keyboard layout - switch (config.locale) { - case 'fr': - log.debug('french keyboard layout'); - this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH; - break; - case 'en': - default: - log.debug('english keyboard layout'); - this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US; - } - - - //bind all events - var self = this; - this.global.on('connect', function () { - self.connected = true; - self.emit('connect'); - }).on('session', function () { - self.emit('session'); - }).on('close', function () { - self.connected = false; - self.emit('close'); - }).on('bitmap', function (bitmaps) { - for(var bitmap in bitmaps) { - var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value; - var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION; - - if (isCompress && config.decompress) { - bitmapData = decompress(bitmaps[bitmap].obj); - isCompress = false; - } - - self.emit('bitmap', { - destTop : bitmaps[bitmap].obj.destTop.value, - destLeft : bitmaps[bitmap].obj.destLeft.value, - destBottom : bitmaps[bitmap].obj.destBottom.value, - destRight : bitmaps[bitmap].obj.destRight.value, - width : bitmaps[bitmap].obj.width.value, - height : bitmaps[bitmap].obj.height.value, - bitsPerPixel : bitmaps[bitmap].obj.bitsPerPixel.value, - isCompress : isCompress, - data : bitmapData - }); - } - }).on('error', function (err) { - log.warn(err.code + '(' + err.message + ')\n' + err.stack); - if (err instanceof error.FatalError) { - throw err; - } - else { - self.emit('error', err); - } - }); + + if (config.autoLogin) { + this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON; + } + + if (config.screen && config.screen.width && config.screen.height) { + this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width; + this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height; + } + + log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value); + + // config keyboard layout + switch (config.locale) { + case 'fr': + log.debug('french keyboard layout'); + this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH; + break; + case 'en': + default: + log.debug('english keyboard layout'); + this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US; + } + + + //bind all events + var self = this; + this.global.on('connect', function () { + self.connected = true; + self.emit('connect'); + }).on('session', function () { + self.emit('session'); + }).on('close', function () { + self.connected = false; + self.emit('close'); + }).on('bitmap', function (bitmaps) { + for (var bitmap in bitmaps) { + var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value; + var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION; + + if (isCompress && config.decompress) { + bitmapData = decompress(bitmaps[bitmap].obj); + isCompress = false; + } + + self.emit('bitmap', { + destTop: bitmaps[bitmap].obj.destTop.value, + destLeft: bitmaps[bitmap].obj.destLeft.value, + destBottom: bitmaps[bitmap].obj.destBottom.value, + destRight: bitmaps[bitmap].obj.destRight.value, + width: bitmaps[bitmap].obj.width.value, + height: bitmaps[bitmap].obj.height.value, + bitsPerPixel: bitmaps[bitmap].obj.bitsPerPixel.value, + isCompress: isCompress, + data: bitmapData + }); + } + }).on('error', function (err) { + log.warn(err.code + '(' + err.message + ')\n' + err.stack); + if (err instanceof error.FatalError) { + throw err; + } + else { + self.emit('error', err); + } + }); } inherits(RdpClient, events.EventEmitter); @@ -197,24 +197,24 @@ inherits(RdpClient, events.EventEmitter); * @param port {integer} destination port */ RdpClient.prototype.connect = function (host, port) { - log.debug('connect to ' + host + ':' + port); - var self = this; - this.bufferLayer.socket.connect(port, host, function () { - // in client mode connection start from x224 layer - self.x224.connect(); - }); - return this; + log.debug('connect to ' + host + ':' + port); + var self = this; + this.bufferLayer.socket.connect(port, host, function () { + // in client mode connection start from x224 layer + self.x224.connect(); + }); + return this; }; /** * Close RDP client */ RdpClient.prototype.close = function () { - if(this.connected) { - this.global.close(); - } - this.connected = false; - return this; + if (this.connected) { + this.global.close(); + } + this.connected = false; + return this; }; /** @@ -225,31 +225,31 @@ RdpClient.prototype.close = function () { * @param isPressed {boolean} state of button */ RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) { - if (!this.connected) - return; - - var event = pdu.data.pointerEvent(); - if (isPressed) { - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN; - } - - switch(button) { - case 1: - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1; - break; - case 2: - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2; - break; - case 3: - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3 - break; - default: - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE; - } - + if (!this.connected) + return; + + var event = pdu.data.pointerEvent(); + if (isPressed) { + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN; + } + + switch (button) { + case 1: + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1; + break; + case 2: + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2; + break; + case 3: + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3 + break; + default: + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE; + } + event.obj.xPos.value = x; event.obj.yPos.value = y; - + this.global.sendInputEvents([event]); }; @@ -260,20 +260,20 @@ RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) { * @param extended {boolenan} extended keys */ RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) { - if (!this.connected) - return; - extended = extended || false; - var event = pdu.data.scancodeKeyEvent(); - event.obj.keyCode.value = code; - - if (!isPressed) { - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; - } - - if (extended) { - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED; + if (!this.connected) + return; + extended = extended || false; + var event = pdu.data.scancodeKeyEvent(); + event.obj.keyCode.value = code; + + if (!isPressed) { + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; } - + + if (extended) { + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED; + } + this.global.sendInputEvents([event]); }; @@ -283,16 +283,16 @@ RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) * @param isPressed {boolean} */ RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) { - if (!this.connected) - return; - - var event = pdu.data.unicodeKeyEvent(); - event.obj.unicode.value = code; - - if (!isPressed) { - event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; - } - this.global.sendInputEvents([event]); + if (!this.connected) + return; + + var event = pdu.data.unicodeKeyEvent(); + event.obj.unicode.value = code; + + if (!isPressed) { + event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE; + } + this.global.sendInputEvents([event]); } /** @@ -304,32 +304,32 @@ RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) { * @param isHorizontal {boolean} */ RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) { - if (!this.connected) - return; - - var event = pdu.data.pointerEvent(); - if (isHorizontal) { + if (!this.connected) + return; + + var event = pdu.data.pointerEvent(); + if (isHorizontal) { event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL; - } - else { - event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL; } - - - if (isNegative) { + else { + event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL; + } + + + if (isNegative) { event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE; - } - + } + event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask) - + event.obj.xPos.value = x; event.obj.yPos.value = y; - + this.global.sendInputEvents([event]); } function createClient(config) { - return new RdpClient(config); + return new RdpClient(config); }; /** @@ -338,28 +338,28 @@ function createClient(config) { * @param socket {net.Socket} */ function RdpServer(config, socket) { - if (!(config.key && config.cert)) { - throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools') - } - this.connected = false; - this.bufferLayer = new layer.BufferLayer(socket); - this.tpkt = new TPKT(this.bufferLayer); - this.x224 = new x224.Server(this.tpkt, config.key, config.cert); - this.mcs = new t125.mcs.Server(this.x224); + if (!(config.key && config.cert)) { + throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools') + } + this.connected = false; + this.bufferLayer = new layer.BufferLayer(socket); + this.tpkt = new TPKT(this.bufferLayer); + this.x224 = new x224.Server(this.tpkt, config.key, config.cert); + this.mcs = new t125.mcs.Server(this.x224); }; inherits(RdpServer, events.EventEmitter); -function createServer (config, next) { - return net.createServer(function (socket) { - next(new RdpServer(config, socket)); - }); +function createServer(config, next) { + return net.createServer(function (socket) { + next(new RdpServer(config, socket)); + }); }; /** * Module exports */ module.exports = { - createClient : createClient, - createServer : createServer + createClient: createClient, + createServer: createServer }; \ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 999b08a3..61c365e5 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1379,6 +1379,10 @@
+
+ + +
@@ -8552,7 +8556,11 @@ x += addHtmlValue("Username", ''); x += addHtmlValue("Password", ''); if ((features2 & 0x00400000) == 0) { x += addHtmlValue("", ''); } - x + ''; + x += ''; + x += MoreStart(); + x += addHtmlValue("Alt Shell", ''); + x += addHtmlValue("Working Dir", ''); + x += MoreEnd(); setDialogMode(2, "RDP Credentials", 3, askRdpCredentialsEx, x); } @@ -8586,11 +8594,11 @@ height = Q('DeskParent').offsetHeight; } if ((currentNode.rdp == 1) && (Q('d2mode').value == 1)) { - connectDesktop(null, 4, { servercred: true, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F }); + connectDesktop(null, 4, { servercred: true, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F, altshell: Q('d2altshell').value, workdir: Q('d2workdir').value }); } else { var savecred = false; if ((features2 & 0x00400000) == 0) { savecred = Q('d2savecred').checked; } - connectDesktop(null, 4, { domain: Q('d2domain').value, username: Q('d2user').value, password: Q('d2pass').value, savecred: savecred, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F }); + connectDesktop(null, 4, { domain: Q('d2domain').value, username: Q('d2user').value, password: Q('d2pass').value, savecred: savecred, width: width, height: height, flags: (desktopsettings.rdpflags != null) ? desktopsettings.rdpflags : 0x2F, altshell: Q('d2altshell').value, workdir: Q('d2workdir').value }); } } @@ -16857,6 +16865,7 @@ // Reset dialog size QS('dialog').width = QS('dialog').top = QS('dialog').left = QS('dialog').right = QS('dialog').bottom = null; + MoreToggle(false); QE('idx_dlgOkButton', true); QV('idx_dlgOkButton', b & 1); @@ -16869,6 +16878,7 @@ for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added QV('dialog', x); if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } } + QV('idx_dlgMoreButtons', (Q('morexxx3') != null)); } function dialogclose(x) { @@ -17470,8 +17480,9 @@ function AddButton(v, f) { return ''; } function AddButton2(v, f) { return ''; } function AddRefreshButton(f) { return ''; } - function MoreStart() { return '▼ ' + "More" + ''; }; + function MoreStart() { return ''; }; + function MoreToggle(v) { QV("morexxx1",!v); QV("morexxx2",v); QV("morexxx3",v); } function getSelectedOptions(sel) { var opts = [], opt; for (var i = 0, len = sel.options.length; i < len; i++) { opt = sel.options[i]; if (opt.selected) { opts.push(opt.value); } } return opts; } function getInstance(x, y) { for (var i in x) { if (x[i]['InstanceID'] == y) return x[i]; } return null; } function getItem(x, y, z) { for (var i in x) { if (x[i][y] == z) return x[i]; } return null; }