从Powershell启动Elevated CMD.exe

Kee*_*rth 3 powershell powershell-2.0 powershell-3.0 powershell-4.0

我正在尝试从PowerShell启动提升的CMD窗口,但遇到了一些问题。以下是我现在拥有的代码。计算机上有一个管理员帐户,其用户名为“ test”,密码为“ test”

$username = "test"
$password = ConvertTo-SecureString "test" -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
Start-Process "cmd.exe" -Credential $cred
Run Code Online (Sandbox Code Playgroud)

对于从用户配置文件运行该脚本时没有管理员权限的应用程序,这一切都可以正常工作,但是在调用cmd.exe时,它将以提升的权限按预期方式启动,然后立即关闭。

我也尝试用以下命令调用它:

Start-Process "cmd.exe" -Credential $cred -ArgumentList '/k'
Run Code Online (Sandbox Code Playgroud)

这也不起作用。
我通过传递一个参数来测试提升的权限,如下所示,它可以正常工作。

Start-Process "cmd.exe" -Credential $cred -ArgumentList 'dir > dir.txt'
Run Code Online (Sandbox Code Playgroud)

这会将dir.txt文件写到C:\ Windows \ System32 \ WindowsPowerShell \ v1.0目录,该目录在用户帐户上被阻止,但对于管理员帐户测试则未被阻止。

非常感谢获得有关持久cmd窗口显示的任何帮助。

谢谢

mkl*_*nt0 5

注意:SomeShinyObject在他的答案中提出了该方法的基本原理,但是他的参数传递技术并不可靠(更新:由于已更正)- 请勿使用脚本块代替字符串 -参见底部。

  • -Verb RunAs是什么使得Start-Process 启动过程变得更加高级

  • 但是,-Verb RunAs不能与-Credential参数组合使用,因此您无法直接控制在哪个用户帐户下进行海拔升高-但这通常不是必需的

    • 如果当前用户是管理员,则提升总是发生在该用户的上下文中,GUI提示仅要求确认
    • 否则,将显示GUI 对话框,询问管理员的用户名和密码(用户名字段为空白)。

安全警告

  • 将密码存储为纯文本通常会带来安全风险。
  • 此外,如果让非管理员用户使用存储的管理员凭据执行以下代码,则可以有效地赋予他们管理权限。

如果你仍然想按照规定实行你的脚本,一个解决办法需要嵌套 2个Start-Process电话

  • 第一个在指定用户(假定是管理用户)中使用(不变)不可见地运行(非恒定)命令-Credential

    • 由于指定的用户是管理员,因此这里不必担心,但是如果-Credential以非管理员用户为目标,则建议还指定一个-WorkingDir参数,表明已知该指定的用户具有访问权限-否则,调用可能会失败(当前位置被保留,并且可能不允许目标用户访问它)。
  • 第二个,嵌入在第一个中,然后用于-Verb RunAs运行提升的目标命令,然后在指定用户的上下文中发生命令。

    • 注意:即使使用包含密码的凭据对象,您仍然会收到是/否UAC提示,以确认意图进行举升 -除非已关闭UAC(不建议这样做)。

    • 工作目录始终是$env:SYSTEMROOT\Windows32; -Verb RunAs甚至忽略一个-WorkingDirectory值;如果要更改到特定目录,请cd在传递给的命令中嵌入命令cmd.exe

此命令完全符合您的要求- 请注意安全警告

# Construct the credentials object
$username = "jdoe"
# CAVEAT: Storing a password as plain text is a security risk in general.
#         Additionally, if you let non-administrative users execute this 
#         code with a stored password, you're effectively giving them
#         administrative rights.
$password = ConvertTo-SecureString "test" -AsPlainText -Force    
$cred = New-Object PSCredential -Args $username, $password

# Start an elevated Command Prompt (cmd) as user $username.
Start-Process powershell.exe -Credential $cred -WindowStyle Hidden `
 '-noprofile -command "Start-Process cmd.exe -Verb RunAs"'
Run Code Online (Sandbox Code Playgroud)

请注意,嵌入的第二个命令作为单个字符串传递给(隐含的)-ArgumentList(aka -Args)参数。

在这种简单情况下,仅使用1级嵌入式引号(字符串内的"实例)'...'并且不需要扩展(字符串插值),传递单个字符串是一个可行的选择,但是使用更复杂的命令引号会比较棘手。

-ArgumentList被定义为type[string[]],即字符串参数数组如果您传递多个 - ,分隔的参数,则是PowerShell为您合成了命令行,这通常使得更容易获得正确的引用,尤其是在涉及变量引用时:

以下命令演示了此技术:它是一种变体,它传递cmd.exe要执行的命令,并在该命令中使用变量引用:

$msg = 'This is an elevated Command Prompt.'

Start-Process powershell.exe -Credential $cred -WindowStyle Hidden -Args `
 '-noprofile', '-command', "Start-Process cmd.exe -Verb RunAs -Args /k, echo, '$msg'"
Run Code Online (Sandbox Code Playgroud)

cmd.exe最终执行的命令(带有高程)为:
cmd /k echo This is an elevated Command Prompt.


可选阅读:为什么不建议使用脚本块代替字符串

tl; dr

  • 不要养成在需要字符串的地方使用脚本块的习惯。虽然方便,但它并不可靠,记住何时以及为什么它将失败并不容易。

乍一看,脚本块({ ... })似乎是一个方便的选项:

Start-Process cmd -ArgumentList { /k echo hi! }
Run Code Online (Sandbox Code Playgroud)

上面的代码cmd /k echo hi!将按预期在新的控制台窗口中执行。语法很方便,因为{ ... }看似提供了一个易于引用的上下文:您可以自由地使用嵌入式 "'实例来构造命令行。

然而,发生了什么幕后是一个脚本块转换为字符串,因为这是争论的(一个或多个)类型-ArgumentList预期,并且当一个脚本块转换为字符串,其字面内容-之间的一切{}-使用
这意味着不会进行字符串插值,因此不能使用变量或子表达式。

尝试通过变量传递命令:

 Start-Process cmd -ArgumentList { /k echo Honey, I`'m $HOME! }
Run Code Online (Sandbox Code Playgroud)

这将执行的是:cmd /k echo Honey, I'm $HOME!- $HOME未扩展。

相比之下,传递插值字符串或参数分别按预期工作:

# As a single string (argument list):
Start-Process cmd -ArgumentList "/k echo Honey, I'm $HOME!"

# As an array of arguments:
Start-Process cmd -ArgumentList /k, echo, "Honey, I'm $HOME!"
Run Code Online (Sandbox Code Playgroud)

$HOME在两种情况下都被扩展(内插),并且
cmd /k echo Honey, I'm C:\Users\jdoe执行类似的操作。