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

# 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