You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
4.2 KiB
84 lines
4.2 KiB
# consoleinject.ps1 - type lines into the console of another process (windows). |
|
# |
|
# The G-106 hidden-console recipe's injection half, committed for test use (G-118 |
|
# item 11 console-reopen coverage in src/tests/shell/testsuites/punkexe/tclshcmd.test): |
|
# detach from our own console (FreeConsole), attach to the target pid's console |
|
# (AttachConsole), open CONIN$ and write key-down/key-up KEY_EVENT records for each |
|
# character of each line plus Enter (VK_RETURN + CR) after every line. Events are |
|
# buffered by the console, so injection may precede the target's console read. |
|
# |
|
# The target pid may be ANY process attached to the target console (e.g. a wrapper |
|
# that launched the real target with redirected std handles in that console). |
|
# |
|
# usage: powershell -NoProfile -ExecutionPolicy Bypass -File consoleinject.ps1 -TargetPid <pid> -TextFile <file> |
|
# stdout: 'INJECT-OK lines=N' on success, 'INJECT-FAIL rc=<1|2|3> lasterr=<winerr>' on failure |
|
# (rc 1 attach failed, 2 CONIN$ open failed, 3 WriteConsoleInput failed); exit code 0/1. |
|
param( |
|
[Parameter(Mandatory=$true)][int]$TargetPid, |
|
[Parameter(Mandatory=$true)][string]$TextFile |
|
) |
|
$ErrorActionPreference = 'Stop' |
|
Add-Type -TypeDefinition @' |
|
using System; |
|
using System.Runtime.InteropServices; |
|
public static class PunkConsoleInject { |
|
[DllImport("kernel32.dll", SetLastError=true)] |
|
public static extern bool FreeConsole(); |
|
[DllImport("kernel32.dll", SetLastError=true)] |
|
public static extern bool AttachConsole(uint dwProcessId); |
|
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)] |
|
public static extern IntPtr CreateFileW(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); |
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] |
|
public struct KEY_EVENT_RECORD { |
|
public int bKeyDown; |
|
public ushort wRepeatCount; |
|
public ushort wVirtualKeyCode; |
|
public ushort wVirtualScanCode; |
|
public char UnicodeChar; |
|
public uint dwControlKeyState; |
|
} |
|
[StructLayout(LayoutKind.Explicit)] |
|
public struct INPUT_RECORD { |
|
[FieldOffset(0)] public ushort EventType; |
|
[FieldOffset(4)] public KEY_EVENT_RECORD KeyEvent; |
|
} |
|
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)] |
|
public static extern bool WriteConsoleInputW(IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, out uint lpNumberOfEventsWritten); |
|
|
|
// returns 0 ok; 1 attach failed; 2 conin open failed; 3 write failed. lastErr = GetLastError of the failing call. |
|
public static int Inject(uint targetPid, string[] lines, out int lastErr) { |
|
lastErr = 0; |
|
FreeConsole(); |
|
if (!AttachConsole(targetPid)) { lastErr = Marshal.GetLastWin32Error(); return 1; } |
|
IntPtr h = CreateFileW("CONIN$", 0xC0000000u, 0x3u, IntPtr.Zero, 3u, 0u, IntPtr.Zero); |
|
if (h == (IntPtr)(-1)) { lastErr = Marshal.GetLastWin32Error(); return 2; } |
|
foreach (string line in lines) { |
|
string full = line + "\r"; |
|
INPUT_RECORD[] recs = new INPUT_RECORD[full.Length * 2]; |
|
int i = 0; |
|
foreach (char c in full) { |
|
ushort vk = (c == '\r') ? (ushort)0x0D : (ushort)0; |
|
KEY_EVENT_RECORD kd = new KEY_EVENT_RECORD(); |
|
kd.bKeyDown = 1; kd.wRepeatCount = 1; kd.wVirtualKeyCode = vk; |
|
kd.wVirtualScanCode = 0; kd.UnicodeChar = c; kd.dwControlKeyState = 0; |
|
KEY_EVENT_RECORD ku = kd; ku.bKeyDown = 0; |
|
recs[i].EventType = 1; recs[i].KeyEvent = kd; i++; |
|
recs[i].EventType = 1; recs[i].KeyEvent = ku; i++; |
|
} |
|
uint written; |
|
if (!WriteConsoleInputW(h, recs, (uint)recs.Length, out written)) { lastErr = Marshal.GetLastWin32Error(); return 3; } |
|
} |
|
return 0; |
|
} |
|
} |
|
'@ |
|
$lines = @(Get-Content -LiteralPath $TextFile) |
|
$lastErr = 0 |
|
$rc = [PunkConsoleInject]::Inject([uint32]$TargetPid, [string[]]$lines, [ref]$lastErr) |
|
if ($rc -ne 0) { |
|
Write-Output "INJECT-FAIL rc=$rc lasterr=$lastErr" |
|
exit 1 |
|
} |
|
Write-Output "INJECT-OK lines=$($lines.Count)" |
|
exit 0
|
|
|