win-terminal.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /*
  2. Copyright 2018-2022 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. var promise = require('promise');
  14. var duplex = require('stream').Duplex;
  15. var SW_HIDE = 0;
  16. var SW_MINIMIZE = 6;
  17. var STARTF_USESHOWWINDOW = 0x1;
  18. var STD_INPUT_HANDLE = -10;
  19. var STD_OUTPUT_HANDLE = -11;
  20. var EVENT_CONSOLE_CARET = 0x4001;
  21. var EVENT_CONSOLE_END_APPLICATION = 0x4007;
  22. var WINEVENT_OUTOFCONTEXT = 0x000;
  23. var WINEVENT_SKIPOWNPROCESS = 0x0002;
  24. var CREATE_NEW_PROCESS_GROUP = 0x200;
  25. var EVENT_CONSOLE_UPDATE_REGION = 0x4002;
  26. var EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003;
  27. var EVENT_CONSOLE_UPDATE_SCROLL = 0x4004;
  28. var EVENT_CONSOLE_LAYOUT = 0x4005;
  29. var EVENT_CONSOLE_START_APPLICATION = 0x4006;
  30. var KEY_EVENT = 0x1;
  31. var MAPVK_VK_TO_VSC = 0;
  32. var WM_QUIT = 0x12;
  33. var GM = require('_GenericMarshal');
  34. var si = GM.CreateVariable(GM.PointerSize == 4 ? 68 : 104);
  35. var pi = GM.CreateVariable(GM.PointerSize == 4 ? 16 : 24);
  36. si.Deref(0, 4).toBuffer().writeUInt32LE(GM.PointerSize == 4 ? 68 : 104); // si.cb
  37. si.Deref(GM.PointerSize == 4 ? 48 : 64, 2).toBuffer().writeUInt16LE(SW_HIDE | SW_MINIMIZE); // si.wShowWindow
  38. si.Deref(GM.PointerSize == 4 ? 44 : 60, 4).toBuffer().writeUInt32LE(STARTF_USESHOWWINDOW); // si.dwFlags;
  39. var MSG = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
  40. function windows_terminal() {
  41. this._ObjectID = 'windows_terminal';
  42. this._user32 = GM.CreateNativeProxy('User32.dll');
  43. this._user32.CreateMethod('DispatchMessageA');
  44. this._user32.CreateMethod('GetMessageA');
  45. this._user32.CreateMethod('MapVirtualKeyA');
  46. this._user32.CreateMethod('PostThreadMessageA');
  47. this._user32.CreateMethod('SetWinEventHook');
  48. this._user32.CreateMethod('ShowWindow');
  49. this._user32.CreateMethod('TranslateMessage');
  50. this._user32.CreateMethod('UnhookWinEvent');
  51. this._user32.CreateMethod('VkKeyScanA');
  52. this._user32.terminal = this;
  53. this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
  54. this._kernel32.CreateMethod('AllocConsole');
  55. this._kernel32.CreateMethod('CreateProcessA');
  56. this._kernel32.CreateMethod('CloseHandle');
  57. this._kernel32.CreateMethod('FillConsoleOutputAttribute');
  58. this._kernel32.CreateMethod('FillConsoleOutputCharacterA');
  59. this._kernel32.CreateMethod('GetConsoleScreenBufferInfo');
  60. this._kernel32.CreateMethod('GetConsoleWindow');
  61. this._kernel32.CreateMethod('GetLastError');
  62. this._kernel32.CreateMethod('GetStdHandle');
  63. this._kernel32.CreateMethod('GetThreadId');
  64. this._kernel32.CreateMethod('ReadConsoleOutputA');
  65. this._kernel32.CreateMethod('SetConsoleCursorPosition');
  66. this._kernel32.CreateMethod('SetConsoleScreenBufferSize');
  67. this._kernel32.CreateMethod('SetConsoleWindowInfo');
  68. this._kernel32.CreateMethod('TerminateProcess');
  69. this._kernel32.CreateMethod('WaitForSingleObject');
  70. this._kernel32.CreateMethod('WriteConsoleInputA');
  71. var currentX = 0;
  72. var currentY = 0;
  73. this._scrx = 0;
  74. this._scry = 0;
  75. this.SendCursorUpdate = function () {
  76. var newCsbi = GM.CreateVariable(22);
  77. if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
  78. if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY)
  79. {
  80. //
  81. // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
  82. // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
  83. //
  84. this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE();
  85. this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE();
  86. }
  87. }
  88. this.ClearScreen = function ()
  89. {
  90. //
  91. // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
  92. // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
  93. //
  94. //
  95. // Reference for GetConsoleScreenBufferInfo can be found at:
  96. // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
  97. //
  98. //
  99. // Reference for FillConsoleOutputCharacter can be found at:
  100. // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter
  101. //
  102. //
  103. // Reference for FillConsoleOutputAttribute can be found at:
  104. // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputattribute
  105. //
  106. //
  107. // Reference for SetConsoleCursorPosition can be found at:
  108. // https://learn.microsoft.com/en-us/windows/console/setconsolecursorposition
  109. //
  110. //
  111. // Reference for SetConsoleWindowInfo can be fount at:
  112. // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
  113. //
  114. var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
  115. if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
  116. var coordScreen = GM.CreateVariable(4);
  117. var dwConSize = CONSOLE_SCREEN_BUFFER_INFO.Deref(0, 2).toBuffer().readUInt16LE(0) * CONSOLE_SCREEN_BUFFER_INFO.Deref(2, 2).toBuffer().readUInt16LE(0);
  118. var cCharsWritten = GM.CreateVariable(4);
  119. // Fill the entire screen with blanks.
  120. if (this._kernel32.FillConsoleOutputCharacterA(this._stdoutput, 32, dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
  121. // Get the current text attribute.
  122. if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
  123. // Set the buffer's attributes accordingly.
  124. if (this._kernel32.FillConsoleOutputAttribute(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO.Deref(8, 2).toBuffer().readUInt16LE(0), dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
  125. // Put the cursor at its home coordinates.
  126. this._kernel32.SetConsoleCursorPosition(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE());
  127. // Put the window to top-left.
  128. var rect = GM.CreateVariable(8);
  129. var srWindow = CONSOLE_SCREEN_BUFFER_INFO.Deref(10, 8).toBuffer();
  130. rect.Deref(4, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(4) - srWindow.readUInt16LE(0));
  131. rect.Deref(6, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(6) - srWindow.readUInt16LE(2));
  132. this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
  133. }
  134. // This does a rudimentary check if the platform is capable of PowerShell
  135. this.PowerShellCapable = function()
  136. {
  137. if (require('os').arch() == 'x64')
  138. {
  139. return (require('fs').existsSync(process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
  140. }
  141. else
  142. {
  143. return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
  144. }
  145. }
  146. // Starts a Legacy Windows Terminal Session
  147. this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget)
  148. {
  149. // The older windows terminal does not support
  150. CONSOLE_SCREEN_WIDTH = 80;
  151. CONSOLE_SCREEN_HEIGHT = 25;
  152. if (this._stream != null)
  153. {
  154. throw ('Concurrent terminal sessions are not supported on Windows.');
  155. }
  156. this.stopping = null;
  157. if (this._kernel32.GetConsoleWindow().Val == 0) {
  158. if (this._kernel32.AllocConsole().Val == 0) {
  159. throw ('AllocConsole failed with: ' + this._kernel32.GetLastError().Val);
  160. }
  161. }
  162. this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
  163. this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
  164. this._connected = false;
  165. // Coord structure can be found at: https://learn.microsoft.com/en-us/windows/console/coord-str
  166. var coordScreen = GM.CreateVariable(4);
  167. coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
  168. coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
  169. var rect = GM.CreateVariable(8);
  170. rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
  171. rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
  172. //
  173. // Reference for SetConsoleWindowInfo can be found at:
  174. // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
  175. //
  176. if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0)
  177. {
  178. throw ('Failed to set Console Screen Size');
  179. }
  180. //
  181. // Reference for SetConsoleScreenBufferSize can be found at:
  182. // https://learn.microsoft.com/en-us/windows/console/setconsolescreenbuffersize
  183. //
  184. if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0)
  185. {
  186. throw ('Failed to set Console Buffer Size');
  187. }
  188. // Hide the console window
  189. this._user32.ShowWindow(this._kernel32.GetConsoleWindow().Val, SW_HIDE);
  190. this.ClearScreen();
  191. this._hookThread(terminalTarget).then(function ()
  192. {
  193. // Hook Ready
  194. this.terminal.StartCommand(this.userArgs[0]);
  195. }, console.log);
  196. this._stream = new duplex(
  197. {
  198. 'write': function (chunk, flush)
  199. {
  200. if (!this.terminal.connected)
  201. {
  202. //console.log('_write: ' + chunk);
  203. if (!this._promise.chunk)
  204. {
  205. this._promise.chunk = [];
  206. }
  207. if (typeof (chunk) == 'string')
  208. {
  209. this._promise.chunk.push(chunk);
  210. } else
  211. {
  212. this._promise.chunk.push(Buffer.alloc(chunk.length));
  213. chunk.copy(this._promise.chunk.peek());
  214. }
  215. this._promise.chunk.peek().flush = flush;
  216. this._promise.then(function ()
  217. {
  218. var buf;
  219. while (this.chunk.length > 0)
  220. {
  221. buf = this.chunk.shift();
  222. this.terminal._WriteBuffer(buf);
  223. buf.flush();
  224. }
  225. });
  226. }
  227. else
  228. {
  229. //console.log('writeNOW: ' + chunk);
  230. this.terminal._WriteBuffer(chunk);
  231. flush();
  232. }
  233. return (true);
  234. },
  235. 'final': function (flush)
  236. {
  237. var p = this.terminal._stop();
  238. p.__flush = flush;
  239. p.then(function () { this.__flush(); });
  240. }
  241. });
  242. this._stream.terminal = this;
  243. this._stream._promise = new promise(function (res, rej) { this._res = res; this._rej = rej; });
  244. this._stream._promise.terminal = this;
  245. this._stream.prependOnceListener('end', function ()
  246. {
  247. this.terminal._stream = null;
  248. });
  249. return (this._stream);
  250. };
  251. this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
  252. {
  253. return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\cmd.exe'));
  254. }
  255. this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
  256. {
  257. if (require('os').arch() == 'x64')
  258. {
  259. if (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'))
  260. {
  261. return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
  262. }
  263. else
  264. {
  265. return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
  266. }
  267. }
  268. else
  269. {
  270. return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
  271. }
  272. }
  273. this._stop = function () {
  274. if (this.stopping) { return (this.stopping); }
  275. //console.log('Stopping Terminal...');
  276. this._ConsoleWinEventProc.removeAllListeners('GlobalCallback');
  277. this.stopping = new promise(function (res, rej) { this._res = res; this._rej = rej; });
  278. var threadID = this._kernel32.GetThreadId(this._user32.SetWinEventHook.async.thread()).Val;
  279. this._user32.PostThreadMessageA(threadID, WM_QUIT, 0, 0);
  280. this._stream.emit('end');
  281. return (this.stopping);
  282. }
  283. //
  284. // This function uses the SetWinEventHook() method, so we can hook
  285. // All events between EVENT_CONSOLE_CARET and EVENT_CONSOLE_END_APPLICATION
  286. //
  287. this._hookThread = function ()
  288. {
  289. //
  290. // Reference for SetWinEventHook() can be found at:
  291. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
  292. //
  293. var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
  294. ret.userArgs = [];
  295. for (var a in arguments)
  296. {
  297. ret.userArgs.push(arguments[a]);
  298. }
  299. ret.terminal = this;
  300. this._ConsoleWinEventProc = GM.GetGenericGlobalCallback(7);
  301. this._ConsoleWinEventProc.terminal = this;
  302. var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
  303. p.ready = ret;
  304. p.terminal = this;
  305. p.then(function (hwinEventHook)
  306. {
  307. if (hwinEventHook.Val == 0)
  308. {
  309. this.ready._rej('Error calling SetWinEventHook');
  310. } else
  311. {
  312. this.terminal.hwinEventHook = hwinEventHook;
  313. this.ready._res();
  314. this.terminal._GetMessage();
  315. }
  316. });
  317. //
  318. // This is the WINEVENTPROC callback for the WinEventHook we set
  319. //
  320. this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime)
  321. {
  322. //
  323. // Reference for WINEVENTPROC can be found at:
  324. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc
  325. //
  326. if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
  327. var buffer = null;
  328. //
  329. // Reference for Console WinEvents can be found at:
  330. // https://learn.microsoft.com/en-us/windows/console/console-winevents
  331. //
  332. switch (dwEvent.Val)
  333. {
  334. case EVENT_CONSOLE_CARET:
  335. // The console caret has moved
  336. break;
  337. case EVENT_CONSOLE_UPDATE_REGION:
  338. // More than one character has changed
  339. if (!this.terminal.connected) {
  340. this.terminal.connected = true;
  341. this.terminal._stream._promise._res();
  342. }
  343. if (this.terminal._scrollTimer == null) {
  344. buffer = this.terminal._GetScreenBuffer(LOWORD(idObject.Val), HIWORD(idObject.Val), LOWORD(idChild.Val), HIWORD(idChild.Val));
  345. //console.log('UPDATE REGION: [Left: ' + LOWORD(idObject.Val) + ' Top: ' + HIWORD(idObject.Val) + ' Right: ' + LOWORD(idChild.Val) + ' Bottom: ' + HIWORD(idChild.Val) + ']');
  346. this.terminal._SendDataBuffer(buffer);
  347. }
  348. break;
  349. case EVENT_CONSOLE_UPDATE_SIMPLE:
  350. // A single character has changed
  351. //console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
  352. 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) };
  353. this.terminal._SendDataBuffer(simplebuffer);
  354. break;
  355. case EVENT_CONSOLE_UPDATE_SCROLL:
  356. // The console has scrolled
  357. //console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
  358. this.terminal._SendScroll(idObject.Val, idChild.Val);
  359. break;
  360. case EVENT_CONSOLE_LAYOUT:
  361. // The console layout has changed.
  362. //console.log('CONSOLE_LAYOUT');
  363. //snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
  364. //SendLayout();
  365. break;
  366. case EVENT_CONSOLE_START_APPLICATION:
  367. // A new console process has started
  368. //console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
  369. //snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
  370. //SendConsoleEvent(dwEvent, idObject, idChild);
  371. break;
  372. case EVENT_CONSOLE_END_APPLICATION:
  373. // A console process has exited
  374. if (idObject.Val == this.terminal._hProcessID)
  375. {
  376. //console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
  377. this.terminal._hProcess = null;
  378. this.terminal._stop().then(function () { console.log('STOPPED'); });
  379. }
  380. break;
  381. default:
  382. //snprintf(Buf, 512, "unknown console event.\r\n");
  383. console.log('Unknown event: ' + dwEvent.Val);
  384. break;
  385. }
  386. //mbstowcs_s(&l, wBuf, Buf, 512);
  387. //OutputDebugString(wBuf);
  388. });
  389. return (ret);
  390. }
  391. // Retrieves a message from the calling thread's message queue
  392. this._GetMessage = function ()
  393. {
  394. //
  395. // Reference for GetMessage() can be found at:
  396. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
  397. //
  398. //
  399. // Reference for TranslateMessage() can be found at:
  400. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-translatemessage
  401. //
  402. //
  403. // Reference for DispatchMessage() can be found at:
  404. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dispatchmessage
  405. //
  406. if (this._user32.abort) { console.log('aborting loop'); return; }
  407. this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret)
  408. {
  409. //console.log('GetMessage Response');
  410. if (ret.Val != 0)
  411. {
  412. if (ret.Val == -1)
  413. {
  414. // handle the error and possibly exit
  415. }
  416. else
  417. {
  418. // Translates virtual-key messages into character messages
  419. //console.log('TranslateMessage');
  420. this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
  421. {
  422. // Dispatches a message to a window procedure
  423. //console.log('DispatchMessage');
  424. this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
  425. {
  426. this.nativeProxy.terminal._GetMessage();
  427. }, console.log);
  428. }, console.log);
  429. }
  430. } else
  431. {
  432. this.nativeProxy.UnhookWinEvent.async(this.nativeProxy.terminal._user32.SetWinEventHook.async, this.nativeProxy.terminal.hwinEventHook)
  433. .then(function ()
  434. {
  435. if (this.nativeProxy.terminal._hProcess == null) { return; }
  436. this.nativeProxy.terminal.stopping._res();
  437. if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0)
  438. {
  439. var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
  440. console.log('Unable to kill Terminal Process, error: ' + e);
  441. }
  442. this.nativeProxy.terminal.stopping = null;
  443. }, function (err)
  444. {
  445. console.log('REJECTED_UnhookWinEvent: ' + err);
  446. });
  447. }
  448. }, function (err)
  449. {
  450. // Get Message Failed
  451. console.log('REJECTED_GETMessage: ' + err);
  452. });
  453. }
  454. this._WriteBuffer = function (buf)
  455. {
  456. for (var i = 0; i < buf.length; ++i)
  457. {
  458. if (typeof (buf) == 'string')
  459. {
  460. this._WriteCharacter(buf.charCodeAt(i), false);
  461. } else
  462. {
  463. this._WriteCharacter(buf[i], false);
  464. }
  465. }
  466. }
  467. this._WriteCharacter = function (key, bControlKey)
  468. {
  469. //
  470. // Reference for WriteConsoleInput() can be found at:
  471. // https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
  472. //
  473. //
  474. // Reference for INPUT_RECORD can be found at:
  475. // https://learn.microsoft.com/en-us/windows/console/input-record-str
  476. //
  477. var rec = GM.CreateVariable(20);
  478. rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
  479. rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
  480. rec.Deref(16, 4).toBuffer().writeUInt32LE(bControlKey); // rec.Event.KeyEvent.dwControlKeyState
  481. rec.Deref(14, 1).toBuffer()[0] = key; // rec.Event.KeyEvent.uChar.AsciiChar
  482. rec.Deref(8, 2).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.wRepeatCount
  483. rec.Deref(10, 2).toBuffer().writeUInt16LE(this._user32.VkKeyScanA(key).Val); // rec.Event.KeyEvent.wVirtualKeyCode
  484. rec.Deref(12, 2).toBuffer().writeUInt16LE(this._user32.MapVirtualKeyA(this._user32.VkKeyScanA(key).Val, MAPVK_VK_TO_VSC).Val);
  485. var dwWritten = GM.CreateVariable(4);
  486. if (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val == 0) { return (false); }
  487. rec.Deref(4, 4).toBuffer().writeUInt16LE(0); // rec.Event.KeyEvent.bKeyDown
  488. return (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val != 0);
  489. }
  490. // Get the current visible screen buffer
  491. this._GetScreenBuffer = function (sx, sy, ex, ey)
  492. {
  493. //
  494. // Reference for GetConsoleScreenBufferInfo() can be found at:
  495. // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
  496. //
  497. //
  498. // Reference for ReadConsoleOutput() can be found at:
  499. // https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
  500. //
  501. var info = GM.CreateVariable(22);
  502. if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
  503. var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
  504. var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
  505. if (arguments[3] == null)
  506. {
  507. // Use Default Parameters
  508. sx = 0;
  509. sy = 0;
  510. ex = nWidth - 1;
  511. ey = nHeight - 1;
  512. } else
  513. {
  514. if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; }
  515. if (this._scry != 0) { sy += this._scry; ey += this._scry; }
  516. this._scrx = this._scry = 0;
  517. }
  518. var nBuffer = GM.CreateVariable((ex - sx + 1) * (ey - sy + 1) * 4);
  519. var size = GM.CreateVariable(4);
  520. size.Deref(0, 2).toBuffer().writeUInt16LE(ex - sx + 1, 0);
  521. size.Deref(2, 2).toBuffer().writeUInt16LE(ey - sy + 1, 0);
  522. var startCoord = GM.CreateVariable(4);
  523. startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0);
  524. startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0);
  525. var region = GM.CreateVariable(8);
  526. region.buffer = region.toBuffer();
  527. region.buffer.writeUInt16LE(sx, 0);
  528. region.buffer.writeUInt16LE(sy, 2);
  529. region.buffer.writeUInt16LE(ex, 4);
  530. region.buffer.writeUInt16LE(ey, 6);
  531. if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0)
  532. {
  533. throw ('Unable to read Console Output');
  534. }
  535. // Lets convert the buffer into something simpler
  536. //var retVal = { data: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), attributes: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), width: dw - dx + 1, height: dh - dy + 1, x: dx, y: dy };
  537. var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
  538. var x, y, line, ifo, tmp, lineWidth = ex - sx + 1;
  539. for (y = 0; y <= (ey - sy) ; ++y)
  540. {
  541. retVal.data.push(Buffer.alloc(lineWidth));
  542. retVal.attributes.push(Buffer.alloc(lineWidth));
  543. line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
  544. for (x = 0; x < lineWidth; ++x)
  545. {
  546. retVal.data.peek()[x] = line[x * 4];
  547. retVal.attributes.peek()[x] = line[2 + (x * 4)];
  548. }
  549. }
  550. return (retVal);
  551. }
  552. this._SendDataBuffer = function (data)
  553. {
  554. // { data, attributes, width, height, x, y }
  555. if (this._stream != null)
  556. {
  557. var dy, line, attr;
  558. for (dy = 0; dy < data.height; ++dy)
  559. {
  560. line = data.data[dy];
  561. attr = data.attributes[dy];
  562. line.s = line.toString();
  563. //line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
  564. //attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
  565. this._stream.push(TranslateLine(data.x + 1, data.y + dy + 1, line, attr));
  566. }
  567. }
  568. }
  569. this._SendScroll = function _SendScroll(dx, dy)
  570. {
  571. //
  572. // Reference for GetConsoleScreenBufferInfo() can be found at:
  573. // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
  574. //
  575. if (this._scrollTimer || this._stream == null) { return; }
  576. var info = GM.CreateVariable(22);
  577. if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
  578. var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
  579. var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
  580. this._stream.push(GetEsc('H', [nHeight - 1, 0]));
  581. for (var i = 0; i > nHeight; ++i) { this._stream.push(Buffer.from('\r\n')); }
  582. var buffer = this._GetScreenBuffer(0, 0, nWidth - 1, nHeight - 1);
  583. this._SendDataBuffer(buffer);
  584. this._scrollTimer = setTimeout(function (self, nw, nh) {
  585. var buffer = self._GetScreenBuffer(0, 0, nw - 1, nh - 1);
  586. self._SendDataBuffer(buffer);
  587. self._scrollTimer = null;
  588. }, 250, this, nWidth, nHeight);
  589. }
  590. this.StartCommand = function StartCommand(target) {
  591. if (this._kernel32.CreateProcessA(GM.CreateVariable(target), 0, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, si, pi).Val == 0)
  592. {
  593. console.log('Error Spawning CMD');
  594. return;
  595. }
  596. this._kernel32.CloseHandle(pi.Deref(GM.PointerSize, GM.PointerSize).Deref()); // pi.hThread
  597. this._hProcess = pi.Deref(0, GM.PointerSize).Deref(); // pi.hProcess
  598. this._hProcessID = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); // pi.dwProcessId
  599. //console.log('Ready => hProcess: ' + this._hProcess._ptr + ' PID: ' + this._hProcessID);
  600. }
  601. }
  602. function LOWORD(val) { return (val & 0xFFFF); }
  603. function HIWORD(val) { return ((val >> 16) & 0xFFFF); }
  604. function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); }
  605. function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); }
  606. function TranslateLine(x, y, data, attributes)
  607. {
  608. var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])];
  609. if (typeof attributes == 'number') { attributes = [attributes]; } // If we get a single attribute, turn it into an array.
  610. for (i = 0; i < data.length; i++)
  611. {
  612. if (lastAttr != attributes[i])
  613. { // To boost performance, if the attribute is the same as the last one, skip this entire part.
  614. fc = (attributes[i] & 0x0007);
  615. fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color
  616. bc = (attributes[i] & 0x0070) >> 4;
  617. bc = ((bc & 0x0001) << 2) + (bc & 0x0002) + ((bc & 0x0004) >> 2); // Background color
  618. rc = (attributes[i] & 0x4000); // Reverse color set
  619. fb = (attributes[i] & 0x0008) >> 3; // Bright foreground set
  620. bb = (attributes[i] & 0x0080); // Bright background set
  621. if (rc != rcolor) { if (rc != 0) { esc.push(7); } else { esc.push(0); fcolor = 7; bcolor = 0; fbright = 0; bbright = 0; } rcolor = rc; } // Reverse Color
  622. if (fc != fcolor) { esc.push(fc + 30); fcolor = fc; } // Set the foreground color if needed
  623. if (bc != bcolor) { esc.push(bc + 40); bcolor = bc; } // Set the background color if needed
  624. if (fb != fbright) { esc.push(2 - fb); fbright = fb; } // Set the bright foreground color if needed
  625. if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed
  626. if (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; }
  627. lastAttr = attributes[i];
  628. }
  629. output.push(Buffer.from(String.fromCharCode(data[i])));
  630. }
  631. return Buffer.concat(output);
  632. }
  633. module.exports = new windows_terminal();