Added workaround for 32 bit windows agent on 64 bit windows, to inject sysnative when callind readdirSync() on %windir%

Updated documentation for windows terminal.
This commit is contained in:
Bryan Roe 2022-10-14 11:18:49 -07:00
parent 601d5e5fcb
commit 06ec0caec4
3 changed files with 262 additions and 74 deletions

View File

@ -47,6 +47,27 @@ var MESHRIGHT_NODESKTOP = 65536;
var pendingSetClip = false; // This is a temporary hack to prevent multiple setclips at the same time to stop the agent from crashing. var pendingSetClip = false; // This is a temporary hack to prevent multiple setclips at the same time to stop the agent from crashing.
function __readdirSync_fix(path)
{
var sysnative = false;
pathstr = require('fs')._fixwinpath(path);
if (pathstr.split('\\*').join('').toLowerCase() == process.env['windir'].toLowerCase()) { sysnative = true; }
var ret = __readdirSync_old(path);
if (sysnative) { ret.push('sysnative'); }
return (ret);
}
if (process.platform == 'win32' && require('_GenericMarshal').PointerSize == 4 && require('os').arch() == 'x64')
{
if(require('fs').readdirSync.version == null)
{
require('fs').__readdirSync_old = require('fs').readdirSync;
require('fs').readdirSync = __readdirSync_fix;
}
}
function bcdOK() { function bcdOK() {
if (process.platform != 'win32') { return (false); } if (process.platform != 'win32') { return (false); }
if (require('os').arch() == 'x64') { if (require('os').arch() == 'x64') {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2018 Intel Corporation Copyright 2018-2022 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -89,21 +89,50 @@ function windows_terminal() {
var newCsbi = GM.CreateVariable(22); var newCsbi = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; } if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY) { if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY)
//wchar_t mywbuf[512]; {
//swprintf(mywbuf, 512, TEXT("csbi.dwCursorPosition.X = %d, csbi.dwCursorPosition.Y = %d, newCsbi.dwCursorPosition.X = %d, newCsbi.dwCursorPosition.Y = %d\r\n"), csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, newCsbi.dwCursorPosition.X, newCsbi.dwCursorPosition.Y); //
//OutputDebugString(mywbuf); // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
//m_viewOffset = newCsbi.srWindow.Top; //
//WriteMoveCursor((SerialAgent *)this->sa, (char)(newCsbi.dwCursorPosition.Y - m_viewOffset), (char)(newCsbi.dwCursorPosition.X - m_viewOffset));
//LowStackSendData((SerialAgent *)(this->sa), "", 0);
this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE(); this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE();
this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE(); this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE();
} }
} }
this.ClearScreen = function () { this.ClearScreen = function ()
{
//
// Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
//
//
// Reference for GetConsoleScreenBufferInfo can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
//
// Reference for FillConsoleOutputCharacter can be found at:
// https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter
//
//
// Reference for FillConsoleOutputAttribute can be found at:
// https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputattribute
//
//
// Reference for SetConsoleCursorPosition can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolecursorposition
//
//
// Reference for SetConsoleWindowInfo can be fount at:
// https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
//
var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22); var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; } if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
@ -132,6 +161,7 @@ function windows_terminal() {
this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect); this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
} }
// This does a rudimentary check if the platform is capable of PowerShell
this.PowerShellCapable = function() this.PowerShellCapable = function()
{ {
if (require('os').arch() == 'x64') if (require('os').arch() == 'x64')
@ -144,6 +174,7 @@ function windows_terminal() {
} }
} }
// Starts a Legacy Windows Terminal Session
this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget) this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget)
{ {
// The older windows terminal does not support // The older windows terminal does not support
@ -164,6 +195,8 @@ function windows_terminal() {
this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE); this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE); this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
this._connected = false; this._connected = false;
// Coord structure can be found at: https://learn.microsoft.com/en-us/windows/console/coord-str
var coordScreen = GM.CreateVariable(4); var coordScreen = GM.CreateVariable(4);
coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH); coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT); coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
@ -172,10 +205,21 @@ function windows_terminal() {
rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1); rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1); rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0) { //
// Reference for SetConsoleWindowInfo can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
//
if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0)
{
throw ('Failed to set Console Screen Size'); throw ('Failed to set Console Screen Size');
} }
if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0) {
//
// Reference for SetConsoleScreenBufferSize can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolescreenbuffersize
//
if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0)
{
throw ('Failed to set Console Buffer Size'); throw ('Failed to set Console Buffer Size');
} }
@ -278,8 +322,16 @@ function windows_terminal() {
return (this.stopping); return (this.stopping);
} }
//
// This function uses the SetWinEventHook() method, so we can hook
// All events between EVENT_CONSOLE_CARET and EVENT_CONSOLE_END_APPLICATION
//
this._hookThread = function () this._hookThread = function ()
{ {
//
// Reference for SetWinEventHook() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
//
var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; }); var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
ret.userArgs = []; ret.userArgs = [];
for (var a in arguments) for (var a in arguments)
@ -292,24 +344,43 @@ function windows_terminal() {
var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
p.ready = ret; p.ready = ret;
p.terminal = this; p.terminal = this;
p.then(function (hwinEventHook) { p.then(function (hwinEventHook)
if (hwinEventHook.Val == 0) { {
if (hwinEventHook.Val == 0)
{
this.ready._rej('Error calling SetWinEventHook'); this.ready._rej('Error calling SetWinEventHook');
} else { } else
{
this.terminal.hwinEventHook = hwinEventHook; this.terminal.hwinEventHook = hwinEventHook;
this.ready._res(); this.ready._res();
this.terminal._GetMessage(); this.terminal._GetMessage();
} }
}); });
this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime) { //
// This is the WINEVENTPROC callback for the WinEventHook we set
//
this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime)
{
//
// Reference for WINEVENTPROC can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc
//
if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; } if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
var buffer = null; var buffer = null;
switch (dwEvent.Val) { //
// Reference for Console WinEvents can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-winevents
//
switch (dwEvent.Val)
{
case EVENT_CONSOLE_CARET: case EVENT_CONSOLE_CARET:
// The console caret has moved
break; break;
case EVENT_CONSOLE_UPDATE_REGION: case EVENT_CONSOLE_UPDATE_REGION:
// More than one character has changed
if (!this.terminal.connected) { if (!this.terminal.connected) {
this.terminal.connected = true; this.terminal.connected = true;
this.terminal._stream._promise._res(); this.terminal._stream._promise._res();
@ -321,25 +392,30 @@ function windows_terminal() {
} }
break; break;
case EVENT_CONSOLE_UPDATE_SIMPLE: case EVENT_CONSOLE_UPDATE_SIMPLE:
// A single character has changed
//console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']'); //console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
var simplebuffer = { data: [ Buffer.alloc(1, LOWORD(idChild.Val)) ], attributes: [ HIWORD(idChild.Val) ], width: 1, height: 1, x: LOWORD(idObject.Val), y: HIWORD(idObject.Val) }; var simplebuffer = { data: [ Buffer.alloc(1, LOWORD(idChild.Val)) ], attributes: [ HIWORD(idChild.Val) ], width: 1, height: 1, x: LOWORD(idObject.Val), y: HIWORD(idObject.Val) };
this.terminal._SendDataBuffer(simplebuffer); this.terminal._SendDataBuffer(simplebuffer);
break; break;
case EVENT_CONSOLE_UPDATE_SCROLL: case EVENT_CONSOLE_UPDATE_SCROLL:
// The console has scrolled
//console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']'); //console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
this.terminal._SendScroll(idObject.Val, idChild.Val); this.terminal._SendScroll(idObject.Val, idChild.Val);
break; break;
case EVENT_CONSOLE_LAYOUT: case EVENT_CONSOLE_LAYOUT:
// The console layout has changed.
//console.log('CONSOLE_LAYOUT'); //console.log('CONSOLE_LAYOUT');
//snprintf( Buf, 512, "Event Console LAYOUT!\r\n"); //snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
//SendLayout(); //SendLayout();
break; break;
case EVENT_CONSOLE_START_APPLICATION: case EVENT_CONSOLE_START_APPLICATION:
// A new console process has started
//console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']'); //console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
//snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild); //snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
//SendConsoleEvent(dwEvent, idObject, idChild); //SendConsoleEvent(dwEvent, idObject, idChild);
break; break;
case EVENT_CONSOLE_END_APPLICATION: case EVENT_CONSOLE_END_APPLICATION:
// A console process has exited
if (idObject.Val == this.terminal._hProcessID) if (idObject.Val == this.terminal._hProcessID)
{ {
//console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']'); //console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
@ -360,18 +436,44 @@ function windows_terminal() {
return (ret); return (ret);
} }
this._GetMessage = function () { // Retrieves a message from the calling thread's message queue
this._GetMessage = function ()
{
//
// Reference for GetMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
//
//
// Reference for TranslateMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-translatemessage
//
//
// Reference for DispatchMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dispatchmessage
//
if (this._user32.abort) { console.log('aborting loop'); return; } if (this._user32.abort) { console.log('aborting loop'); return; }
this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret) { this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret)
{
//console.log('GetMessage Response'); //console.log('GetMessage Response');
if (ret.Val != 0) { if (ret.Val != 0)
if (ret.Val == -1) { {
if (ret.Val == -1)
{
// handle the error and possibly exit // handle the error and possibly exit
} else { }
else
{
// Translates virtual-key messages into character messages
//console.log('TranslateMessage'); //console.log('TranslateMessage');
this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () { this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
{
// Dispatches a message to a window procedure
//console.log('DispatchMessage'); //console.log('DispatchMessage');
this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () { this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
{
this.nativeProxy.terminal._GetMessage(); this.nativeProxy.terminal._GetMessage();
}, console.log); }, console.log);
}, console.log); }, console.log);
@ -384,7 +486,8 @@ function windows_terminal() {
if (this.nativeProxy.terminal._hProcess == null) { return; } if (this.nativeProxy.terminal._hProcess == null) { return; }
this.nativeProxy.terminal.stopping._res(); this.nativeProxy.terminal.stopping._res();
if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0) { if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0)
{
var e = this.nativeProxy.terminal._kernel32.GetLastError().Val; var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
console.log('Unable to kill Terminal Process, error: ' + e); console.log('Unable to kill Terminal Process, error: ' + e);
} }
@ -394,22 +497,38 @@ function windows_terminal() {
console.log('REJECTED_UnhookWinEvent: ' + err); console.log('REJECTED_UnhookWinEvent: ' + err);
}); });
} }
}, function (err) { }, function (err)
{
// Get Message Failed // Get Message Failed
console.log('REJECTED_GETMessage: ' + err); console.log('REJECTED_GETMessage: ' + err);
}); });
} }
this._WriteBuffer = function (buf) {
for (var i = 0; i < buf.length; ++i) { this._WriteBuffer = function (buf)
if (typeof (buf) == 'string') { {
for (var i = 0; i < buf.length; ++i)
{
if (typeof (buf) == 'string')
{
this._WriteCharacter(buf.charCodeAt(i), false); this._WriteCharacter(buf.charCodeAt(i), false);
} else { } else
{
this._WriteCharacter(buf[i], false); this._WriteCharacter(buf[i], false);
} }
} }
} }
this._WriteCharacter = function (key, bControlKey) this._WriteCharacter = function (key, bControlKey)
{ {
//
// Reference for WriteConsoleInput() can be found at:
// https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
//
//
// Reference for INPUT_RECORD can be found at:
// https://learn.microsoft.com/en-us/windows/console/input-record-str
//
var rec = GM.CreateVariable(20); var rec = GM.CreateVariable(20);
rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
@ -427,20 +546,33 @@ function windows_terminal() {
} }
// Get the current visible screen buffer // Get the current visible screen buffer
this._GetScreenBuffer = function (sx, sy, ex, ey) { this._GetScreenBuffer = function (sx, sy, ex, ey)
{
//
// Reference for GetConsoleScreenBufferInfo() can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
//
// Reference for ReadConsoleOutput() can be found at:
// https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
//
var info = GM.CreateVariable(22); var info = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); } if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1; var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1; var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
if (arguments[3] == null) { if (arguments[3] == null)
{
// Use Default Parameters // Use Default Parameters
sx = 0; sx = 0;
sy = 0; sy = 0;
ex = nWidth - 1; ex = nWidth - 1;
ey = nHeight - 1; ey = nHeight - 1;
} else { } else
{
if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; } if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; }
if (this._scry != 0) { sy += this._scry; ey += this._scry; } if (this._scry != 0) { sy += this._scry; ey += this._scry; }
this._scrx = this._scry = 0; this._scrx = this._scry = 0;
@ -462,7 +594,8 @@ function windows_terminal() {
region.buffer.writeUInt16LE(ex, 4); region.buffer.writeUInt16LE(ex, 4);
region.buffer.writeUInt16LE(ey, 6); region.buffer.writeUInt16LE(ey, 6);
if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0) { if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0)
{
throw ('Unable to read Console Output'); throw ('Unable to read Console Output');
} }
@ -472,12 +605,14 @@ function windows_terminal() {
var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy }; var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
var x, y, line, ifo, tmp, lineWidth = ex - sx + 1; var x, y, line, ifo, tmp, lineWidth = ex - sx + 1;
for (y = 0; y <= (ey - sy) ; ++y) { for (y = 0; y <= (ey - sy) ; ++y)
{
retVal.data.push(Buffer.alloc(lineWidth)); retVal.data.push(Buffer.alloc(lineWidth));
retVal.attributes.push(Buffer.alloc(lineWidth)); retVal.attributes.push(Buffer.alloc(lineWidth));
line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer(); line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
for (x = 0; x < lineWidth; ++x) { for (x = 0; x < lineWidth; ++x)
{
retVal.data.peek()[x] = line[x * 4]; retVal.data.peek()[x] = line[x * 4];
retVal.attributes.peek()[x] = line[2 + (x * 4)]; retVal.attributes.peek()[x] = line[2 + (x * 4)];
} }
@ -507,6 +642,11 @@ function windows_terminal() {
this._SendScroll = function _SendScroll(dx, dy) this._SendScroll = function _SendScroll(dx, dy)
{ {
//
// Reference for GetConsoleScreenBufferInfo() can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
if (this._scrollTimer || this._stream == null) { return; } if (this._scrollTimer || this._stream == null) { return; }
var info = GM.CreateVariable(22); var info = GM.CreateVariable(22);
@ -546,12 +686,15 @@ function LOWORD(val) { return (val & 0xFFFF); }
function HIWORD(val) { return ((val >> 16) & 0xFFFF); } function HIWORD(val) { return ((val >> 16) & 0xFFFF); }
function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); } function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); }
function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); } function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); }
function TranslateLine(x, y, data, attributes) { function TranslateLine(x, y, data, attributes)
{
var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])]; var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])];
if (typeof attributes == 'number') { attributes = [ attributes ]; } // If we get a single attribute, turn it into an array. if (typeof attributes == 'number') { attributes = [attributes]; } // If we get a single attribute, turn it into an array.
for (i = 0; i < data.length; i++) { for (i = 0; i < data.length; i++)
if (lastAttr != attributes[i]) { // To boost performance, if the attribute is the same as the last one, skip this entire part. {
if (lastAttr != attributes[i])
{ // To boost performance, if the attribute is the same as the last one, skip this entire part.
fc = (attributes[i] & 0x0007); fc = (attributes[i] & 0x0007);
fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color
bc = (attributes[i] & 0x0070) >> 4; bc = (attributes[i] & 0x0070) >> 4;

View File

@ -47,20 +47,20 @@ function vt()
var GM = require('_GenericMarshal'); var GM = require('_GenericMarshal');
var k32 = GM.CreateNativeProxy('kernel32.dll'); var k32 = GM.CreateNativeProxy('kernel32.dll');
k32.CreateMethod('CancelIoEx'); k32.CreateMethod('CancelIoEx'); // https://learn.microsoft.com/en-us/windows/win32/fileio/cancelioex-func
k32.CreateMethod('CreatePipe'); k32.CreateMethod('CreatePipe'); // https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
k32.CreateMethod('CreateProcessW'); k32.CreateMethod('CreateProcessW'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
k32.CreateMethod('CreatePseudoConsole'); k32.CreateMethod('CreatePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/createpseudoconsole
k32.CreateMethod('CloseHandle'); k32.CreateMethod('CloseHandle'); // https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
k32.CreateMethod('ClosePseudoConsole'); k32.CreateMethod('ClosePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/closepseudoconsole
k32.CreateMethod('GetProcessHeap'); k32.CreateMethod('GetProcessHeap'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap
k32.CreateMethod('HeapAlloc'); k32.CreateMethod('HeapAlloc'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc
k32.CreateMethod('InitializeProcThreadAttributeList'); k32.CreateMethod('InitializeProcThreadAttributeList'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist
k32.CreateMethod('ResizePseudoConsole'); k32.CreateMethod('ResizePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/resizepseudoconsole
k32.CreateMethod('UpdateProcThreadAttribute'); k32.CreateMethod('UpdateProcThreadAttribute'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute
k32.CreateMethod('WriteFile'); k32.CreateMethod('WriteFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
k32.CreateMethod('ReadFile'); k32.CreateMethod('ReadFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
k32.CreateMethod('TerminateProcess'); k32.CreateMethod('TerminateProcess'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
var ret = { _h: GM.CreatePointer(), _consoleInput: GM.CreatePointer(), _consoleOutput: GM.CreatePointer(), _input: GM.CreatePointer(), _output: GM.CreatePointer(), k32: k32 }; var ret = { _h: GM.CreatePointer(), _consoleInput: GM.CreatePointer(), _consoleOutput: GM.CreatePointer(), _input: GM.CreatePointer(), _output: GM.CreatePointer(), k32: k32 };
var attrSize = GM.CreateVariable(8); var attrSize = GM.CreateVariable(8);
@ -77,18 +77,31 @@ function vt()
throw ('Error calling CreatePseudoConsole()'); throw ('Error calling CreatePseudoConsole()');
} }
//
// Reference for STARTUPINFOEXW
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-startupinfoexw
//
//
// Reference for STARTUPINFOW
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow
//
k32.InitializeProcThreadAttributeList(0, 1, 0, attrSize); k32.InitializeProcThreadAttributeList(0, 1, 0, attrSize);
attrList = GM.CreateVariable(attrSize.toBuffer().readUInt32LE()); attrList = GM.CreateVariable(attrSize.toBuffer().readUInt32LE());
var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72); var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72); // Create Structure, 64 bits is 112 bytes, 32 bits is 72 bytes
startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0); startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0); // Write buffer size
attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer()); attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer()); // Write the reference to STARTUPINFOEX
if (k32.InitializeProcThreadAttributeList(attrList, 1, 0, attrSize).Val != 0) if (k32.InitializeProcThreadAttributeList(attrList, 1, 0, attrSize).Val != 0)
{ {
if (k32.UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, ret._h.Deref(), GM.PointerSize, 0, 0).Val != 0) if (k32.UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, ret._h.Deref(), GM.PointerSize, 0, 0).Val != 0)
{ {
if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0) if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0) // Create the process to run in the pseudoconsole
{ {
//
// Create a Stream Object, to be able to read/write data to the pseudoconsole
//
ret._startupinfoex = startupinfoex; ret._startupinfoex = startupinfoex;
ret._process = pi.Deref(0); ret._process = pi.Deref(0);
ret._pid = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); ret._pid = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE();
@ -111,6 +124,10 @@ function vt()
flush(); flush();
} }
}); });
//
// The ProcessInfo object is signaled when the process exits
//
ds._obj = ret; ds._obj = ret;
ret._waiter = require('DescriptorEvents').addDescriptor(pi.Deref(0)); ret._waiter = require('DescriptorEvents').addDescriptor(pi.Deref(0));
ret._waiter.ds = ds; ret._waiter.ds = ds;
@ -151,6 +168,7 @@ function vt()
ds._rpbufRead = GM.CreateVariable(4); ds._rpbufRead = GM.CreateVariable(4);
ds.__read = function __read() ds.__read = function __read()
{ {
// Asyncronously read data from the pseudoconsole
this._rp = this.terminal.k32.ReadFile.async(this.terminal._output.Deref(), this._rpbuf, this._rpbuf._size, this._rpbufRead, 0); this._rp = this.terminal.k32.ReadFile.async(this.terminal._output.Deref(), this._rpbuf, this._rpbuf._size, this._rpbufRead, 0);
this._rp.then(function () this._rp.then(function ()
{ {
@ -173,6 +191,8 @@ function vt()
} }
throw ('Internal Error'); throw ('Internal Error');
} }
// This evaluates whether or not the powershell binary exists
this.PowerShellCapable = function () this.PowerShellCapable = function ()
{ {
if (require('os').arch() == 'x64') if (require('os').arch() == 'x64')
@ -184,10 +204,14 @@ function vt()
return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe')); return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
} }
} }
// Start the PseudoConsole with the Command Prompt
this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT) this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
{ {
return (this.Create(process.env['windir'] + '\\System32\\cmd.exe', CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)); return (this.Create(process.env['windir'] + '\\System32\\cmd.exe', CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT));
} }
// Start the PseduoConsole with PowerShell
this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT) this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
{ {
if (require('os').arch() == 'x64') if (require('os').arch() == 'x64')