Hinweis:
Die folgende Lösung funktioniert mit jedem externen Programm und erfasst die Ausgabe immer als Text .
Informationen zum Aufrufen einer anderen PowerShell-Instanz und zum Erfassen ihrer Ausgabe als Rich-Objekte (mit Einschränkungen) finden Sie in der Variantenlösung im unteren Abschnitt oder in der hilfreichen Antwort von Mathias R. Jessen , die das PowerShell-SDK verwendet .
Hier ist ein Proof-of-Concept, der auf der direkten Verwendung der Typen System.Diagnostics.Process
und System.Diagnostics.ProcessStartInfo
.NET zum Erfassen der Prozessausgabe im Speicher basiert (wie in Ihrer Frage angegeben, Start-Process
ist keine Option, da nur das Erfassen der Ausgabe in Dateien unterstützt wird , wie in dieser Antwort gezeigt ). ::
Hinweis:
Aufgrund der Ausführung als anderer Benutzer wird dies nur unter Windows (ab .NET Core 3.1) unterstützt, jedoch in beiden PowerShell-Editionen.
Da .WindowStyle
der Befehl als anderer Benutzer ausgeführt werden muss und die Ausgabe erfasst werden muss, kann er nicht zum Ausführen des ausgeblendeten Befehls verwendet werden (da die Verwendung .WindowStyle
erforderlich .UseShellExecute
ist $true
, was mit diesen Anforderungen nicht kompatibel ist). Da jedoch alle Ausgaben erfasst werden , führt die Einstellung .CreateNoNewWindow
auf $true
effektiv zu einer versteckten Ausführung.
# Get the target user's name and password.
$cred = Get-Credential
# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
# For demo purposes, use a simple `cmd.exe` command that echoes the username.
# See the bottom section for a call to `powershell.exe`.
FileName = 'cmd.exe'
Arguments = '/c echo %USERNAME%'
# Set this to a directory that the target user
# is permitted to access.
WorkingDirectory = 'C:\' #'
# Ask that output be captured in the
# .StandardOutput / .StandardError properties of
# the Process object created later.
UseShellExecute = $false # must be $false
RedirectStandardOutput = $true
RedirectStandardError = $true
# Uncomment this line if you want the process to run effectively hidden.
# CreateNoNewWindow = $true
# Specify the user identity.
# Note: If you specify a UPN in .UserName
# (user@doamin.com), set .Domain to $null
Domain = $env:USERDOMAIN
UserName = $cred.UserName
Password = $cred.Password
}
# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)
# Read the captured standard output.
# By reading to the *end*, this implicitly waits for (near) termination
# of the process.
# Do NOT use $ps.WaitForExit() first, as that can result in a deadlock.
$stdout = $ps.StandardOutput.ReadToEnd()
# Uncomment the following lines to report the process' exit code.
# $ps.WaitForExit()
# "Process exit code: $($ps.ExitCode)"
"Running ``cmd /c echo %USERNAME%`` as user $($cred.UserName) yielded:"
$stdout
Das Obige ergibt ungefähr Folgendes, was zeigt, dass der Prozess mit der angegebenen Benutzeridentität erfolgreich ausgeführt wurde:
Running `cmd /c echo %USERNAME%` as user jdoe yielded:
jdoe
Da Sie eine andere PowerShell- Instanz aufrufen , möchten Sie möglicherweise die Fähigkeit der PowerShell-CLI nutzen , die Ausgabe im CLIXML-Format darzustellen, wodurch die Ausgabe in umfangreiche Objekte deserialisiert werden kann , wenn auch mit eingeschränkter Typentreue , wie in dieser Antwort erläutert .
# Get the target user's name and password.
$cred = Get-Credential
# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
# Invoke the PowerShell CLI with a simple sample command
# that calls `Get-Date` to output the current date as a [datetime] instance.
FileName = 'powershell.exe'
# `-of xml` asks that the output be returned as CLIXML,
# a serialization format that allows deserialization into
# rich objects.
Arguments = '-of xml -noprofile -c Get-Date'
# Set this to a directory that the target user
# is permitted to access.
WorkingDirectory = 'C:\' #'
# Ask that output be captured in the
# .StandardOutput / .StandardError properties of
# the Process object created later.
UseShellExecute = $false # must be $false
RedirectStandardOutput = $true
RedirectStandardError = $true
# Uncomment this line if you want the process to run effectively hidden.
# CreateNoNewWindow = $true
# Specify the user identity.
# Note: If you specify a UPN in .UserName
# (user@doamin.com), set .Domain to $null
Domain = $env:USERDOMAIN
UserName = $cred.UserName
Password = $cred.Password
}
# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)
# Read the captured standard output, in CLIXML format,
# stripping the `#` comment line at the top (`#< CLIXML`)
# which the deserializer doesn't know how to handle.
$stdoutCliXml = $ps.StandardOutput.ReadToEnd() -replace '^#.*\r?\n'
# Uncomment the following lines to report the process' exit code.
# $ps.WaitForExit()
# "Process exit code: $($ps.ExitCode)"
# Use PowerShell's deserialization API to
# "rehydrate" the objects.
$stdoutObjects = [Management.Automation.PSSerializer]::Deserialize($stdoutCliXml)
"Running ``Get-Date`` as user $($cred.UserName) yielded:"
$stdoutObjects
"`nas data type:"
$stdoutObjects.GetType().FullName
Die obigen Ausgaben geben ungefähr Folgendes aus und zeigen, dass die von [datetime]
( System.DateTime
) ausgegebene Instanz ( ) Get-Date
als solche deserialisiert wurde:
Running `Get-Date` as user jdoe yielded:
Friday, March 27, 2020 6:26:49 PM
as data type:
System.DateTime